/*******************************************************************************
* 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.rc.common;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.UnknownHostException;
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.eclipse.jubula.communication.internal.Communicator;
import org.eclipse.jubula.communication.internal.listener.ICommunicationErrorListener;
import org.eclipse.jubula.communication.internal.message.AUTServerStateMessage;
import org.eclipse.jubula.communication.internal.message.AUTStartMessage;
import org.eclipse.jubula.communication.internal.message.ChangeAUTModeMessage;
import org.eclipse.jubula.communication.internal.message.Message;
import org.eclipse.jubula.rc.common.adaptable.AdapterFactoryRegistry;
import org.eclipse.jubula.rc.common.adaptable.IAdapterFactory;
import org.eclipse.jubula.rc.common.commands.AUTStartCommand;
import org.eclipse.jubula.rc.common.commands.ChangeAUTModeCommand;
import org.eclipse.jubula.rc.common.driver.IRobot;
import org.eclipse.jubula.rc.common.exception.ComponentNotFoundException;
import org.eclipse.jubula.rc.common.listener.AUTEventListener;
import org.eclipse.jubula.rc.common.listener.BaseAUTListener;
import org.eclipse.jubula.rc.common.listener.IAutListenerAppender;
import org.eclipse.jubula.rc.common.registration.AgentRegisterAut;
import org.eclipse.jubula.rc.common.registration.IRegisterAut;
import org.eclipse.jubula.rc.common.tester.adapter.interfaces.IComponent;
import org.eclipse.jubula.tools.internal.constants.AUTServerExitConstants;
import org.eclipse.jubula.tools.internal.constants.CommandConstants;
import org.eclipse.jubula.tools.internal.constants.StringConstants;
import org.eclipse.jubula.tools.internal.constants.TimingConstantsServer;
import org.eclipse.jubula.tools.internal.exception.CommunicationException;
import org.eclipse.jubula.tools.internal.exception.JBVersionException;
import org.eclipse.jubula.tools.internal.objects.IComponentIdentifier;
import org.eclipse.jubula.tools.internal.registration.AutIdentifier;
import org.eclipse.jubula.tools.internal.utils.ClassPathHacker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The AutServer controlling the AUT. <br>
* A quasi singleton: the instance is created from main(). <br>
* Expected arguments to main are, see also
* StartAUTServerCommand.createCmdArray():
* <ul>
* <li>The name of host where the client is running on, must be InetAddress
* conform.</li>
* <li>The port the JubulaClient is listening to.</li>
* <li>The main class of the AUT.</li>
* <li>Any further arguments are interpreted as arguments to the AUT.</li>
* <ul>
* When a connection to the JubulaClient could made, any errors will send as a
* message to the JubulaClient.
*
* Changing the mode to OBJECT_MAPPING results in installing an AWTEventListener
* (an instance of <code>MappingListener</code>). For simplification the
* virtual machine is closed without sending a message to the client when an
* error occurs during the installation of the AWTEventListener. The exitcode is
* the appropriate EXIT_* constant
*
* Changing the mode to TESTING removes the installed MappingListener.
*
* @author BREDEX GmbH
* @created 10.07.2006
*/
public abstract class AUTServer {
/** the logger */
private static Logger log = LoggerFactory.getLogger(AUTServer.class);
/** the instance */
private static AUTServer instance = null;
/** the name of the correct autServer class */
private static String autServerName;
/** the communicator to use to communicate with the ITE */
private Communicator m_iteCommunicator;
/** the communicator to use to communicate to the AUT-Agent*/
private Communicator m_autAgentCommunicator;
/** the listener to the client communicator */
private ICommunicationErrorListener m_communicationListener;
/** the listener to the agent communicator */
private ICommunicationErrorListener m_serverCommunicationListener;
/** the name of the main class of the AUT */
private String m_autMainClassName;
/** the class of the AUT, containing the main - method */
private Class<?> m_autMainClass;
/** the main method of the AUT to invoke*/
private Method m_autMainMethod;
/** Thread in which the AUT runs */
private Thread m_autThread;
/** the args for the AUT */
private String[] m_autArgs;
/** is the object mapping used from the Agent? */
private boolean m_isAgentObjectMapping = false;
/**
* the mode the AUTserver is in, see constants in ChangeAUTModeMessage, the
* default is TESTING.
*/
private int m_mode = ChangeAUTModeMessage.TESTING;
/** Indicator whether the AUT is running or not */
private boolean m_isAutRunning = false;
/** true if open, false otherwise */
private boolean m_isObservingDialogOpen = false;
/** timestamp for dynamic WaitForWindow-Timeout for OberservMode*/
private long m_observTimestamp = 0;
/** the AWTEventLister for mode OBJECT_MAPPING */
private AUTEventListener m_mappingListener;
/** the AWTEventLister for mode RECORD_MODE */
private AUTEventListener m_recordListener;
/** the AWTEventLister for mode CHECK_MODE */
private AUTEventListener m_checkListener;
/** true, if this method was called by RcpAccessor Plug-in */
private boolean m_isRcpAccessible;
/** true, if the main method of AUTServer was called by Java Agent */
private boolean m_isAgentSet = false;
/** appenders that will be called when the Inspector is activated */
private List<IAutListenerAppender> m_inspectorListenerAppenders =
new ArrayList<IAutListenerAppender>();
/** the hostname of the AUT-Agent to use for registration */
private String m_autAgentHost;
/** the port number of the AUT-Agent to use for registration */
private String m_autAgentPort;
/** the AUT ID to use for registration with the AUT-Agent */
private String m_autID;
/** List for error messages during extension loading **/
private List<String> m_errors = new ArrayList<String>();
/** List for error messages during extension loading **/
private List<String> m_warnings = new ArrayList<String>();
/** ClassLoader to load external (user-supplied) jars */
private ClassLoader m_externalLoader = null;
/** reference to the component at which an error occured */
private WeakReference<IComponent> m_errorComponent = null;
/**
* private constructor instantiates the listeners
* @param mappingListener new instance of toolkit mapping server
* @param recordListener new instance of toolkit record server
* @param checkListener new instance of toolkit check server
*/
protected AUTServer(AUTEventListener mappingListener,
AUTEventListener recordListener, AUTEventListener checkListener) {
m_communicationListener = new ClientCommunicationListener();
m_serverCommunicationListener = new AgentCommunicationListener();
m_mappingListener = mappingListener;
m_recordListener = recordListener;
m_checkListener = checkListener;
initExternalLoader();
}
/** Initializes the classloader of the user-supplied jars */
private void initExternalLoader() {
try {
// The location of the rc.common_*.jar:
String start = AUTServer.class.getProtectionDomain()
.getCodeSource().getLocation().getPath();
if (System.getProperty("os.name").startsWith("Win")) { //$NON-NLS-1$ //$NON-NLS-2$
// Java cannot properly determine the path for Windows
start = start.substring(1, start.length());
}
start = start.replaceFirst("plugins/[^/]*$", "externaljars"); //$NON-NLS-1$ //$NON-NLS-2$
// The location of the externaljars:
File dir = new File(start);
File[] res = dir.listFiles(new FilenameFilter() {
public boolean accept(File directory, String name) {
return name.endsWith(".jar"); //$NON-NLS-1$
}
});
if (res == null) {
// Can't load the external jars. Just ignore them...
return;
}
ArrayList<URL> urls = new ArrayList<URL>();
for (int i = 0; i < res.length; i++) {
try {
urls.add(res[i].toURI().toURL());
} catch (MalformedURLException e) {
// just ignore, we simply won't load the jar
}
}
if (!urls.isEmpty()) {
m_externalLoader = new URLClassLoader(urls.toArray(new URL[0]));
}
} catch (Exception e) {
// Could not load the external jars. Just ignore them...
}
}
/**
* puts the given arguments to member variables
* @param args the args given to main()
*/
protected void setArgs(String args[]) {
m_autMainClassName = args[Constants.ARG_AUTMAIN];
m_autAgentHost = args[Constants.ARG_REG_HOST];
m_autAgentPort = args[Constants.ARG_REG_PORT];
m_autID = args[Constants.ARG_AUT_NAME];
// arguments for the AUT, is >= 0, see definition of the constants
int numberAutArgs = args.length - Constants.MIN_ARGS_REQUIRED;
m_autArgs = new String[numberAutArgs];
for (int i = 0; i < numberAutArgs; i++) {
m_autArgs[i] = args[Constants.MIN_ARGS_REQUIRED + i];
}
// check if agent is used
if (args.length == Constants.MIN_ARGS_REQUIRED
&& args[Constants.ARG_AGENT_SET].equals(
CommandConstants.RC_COMMON_AGENT_ACTIVE)) {
m_isAgentSet = true;
}
}
/**
* @return Returns the autArgs.
*/
public String[] getAutArgs() {
return m_autArgs;
}
/**
* @return Returns the autMainClassName.
*/
public String getAutMainClassName() {
return m_autMainClassName;
}
/**
* @return Returns the autMainClass.
*/
public Class<?> getAutMainClass() {
return m_autMainClass;
}
/**
* @return Returns the autMainMethod.
*/
public Method getAutMainMethod() {
return m_autMainMethod;
}
/**
* @return Returns the communicator.
*/
public synchronized Communicator getCommunicator() {
if (m_isAgentObjectMapping) {
return m_autAgentCommunicator;
}
return m_iteCommunicator;
}
/**
* @return Returns the communicator.
*/
public synchronized Communicator getServerCommunicator() {
return m_autAgentCommunicator;
}
/**
* @return The mapping listener given to the constructor.
*/
protected AUTEventListener getMappingListener() {
return m_mappingListener;
}
/**
* Method to get the single instance of this class. This also once
* initializes the adapter factory registry.
*
* @return the instance of this Singleton
*/
public static AUTServer getInstance() {
if (instance == null) {
try {
instance = (AUTServer)Class.forName(autServerName)
.newInstance();
} catch (ClassNotFoundException cnfe) {
log.error("creating an AUTServer sharedInstance for " //$NON-NLS-1$
+ autServerName + "failed:" + //$NON-NLS-1$);
cnfe.getMessage());
} catch (InstantiationException ie) {
log.error("creating an AUTServer sharedInstance for " //$NON-NLS-1$
+ autServerName + "failed:" + //$NON-NLS-1$);
ie.getMessage());
} catch (IllegalAccessException iae) {
log.error("creating an AUTServer sharedInstance for " //$NON-NLS-1$
+ autServerName + "failed:" + //$NON-NLS-1$);
iae.getMessage());
}
AdapterFactoryRegistry.initRegistration();
}
return instance;
}
/**
* main method:
* <p>
* 1. check args and store args
* <p>
* 2. call start
*
* @param args
* - the args
*/
public static void main(String[] args) {
validateAndLogMainArgsCount(args, Constants.MIN_ARGS_REQUIRED);
autServerName = args[Constants.ARG_AUTSERVER_NAME];
AUTServer.getInstance().setArgs(args);
AUTServer.getInstance().start(false);
}
/**
* @param args
* the args to validate
* @param noOfRequiredArgs
* the no of expected args to get
*/
protected static void validateAndLogMainArgsCount(String[] args,
int noOfRequiredArgs) {
if (args.length < noOfRequiredArgs) {
log.error("wrong number of arguments: " //$NON-NLS-1$
+ "must be at least " //$NON-NLS-1$
+ String.valueOf(noOfRequiredArgs)
+ ", but were " + Arrays.asList(args).toString()); //$NON-NLS-1$
System.exit(AUTServerExitConstants.EXIT_INVALID_NUMBER_OF_ARGS);
}
if (log.isDebugEnabled()) {
StringBuffer logMessage = new StringBuffer(
"Arguments to AUTServer\n"); //$NON-NLS-1$
for (int i = 0; i < args.length; i++) {
logMessage.append(String.valueOf(i));
logMessage.append(args[i] + "\n"); //$NON-NLS-1$
}
log.debug(logMessage.toString());
}
if (log.isInfoEnabled()) {
log.info(System.getProperty("java.version")); //$NON-NLS-1$
log.info("user.dir:" + //$NON-NLS-1$
System.getProperty("user.dir")); //$NON-NLS-1$
}
}
/**
* Starts the AUTServer in its own Thread with its own ClassLoader.
*/
public void startAUT() {
if (isRcpAccessible()) {
return;
}
if (isAgentSet()) {
//if java agent is to be used, start tasks without invoking AUT again
startToolkitThread();
addToolKitEventListenerToAUT();
return;
}
setAutThread(new Thread(new Runnable() {
public void run() {
try {
startTasks();
} catch (ExceptionInInitializerError e) {
log.error(String.valueOf(e), e);
System.exit(AUTServerExitConstants.AUT_START_ERROR);
} catch (InvocationTargetException e) {
String exception = String.valueOf(e);
String targetException = String.valueOf(
e.getTargetException());
String message = exception + " TargetException: " //$NON-NLS-1$
+ targetException;
log.error(message, e);
System.exit(AUTServerExitConstants.AUT_START_ERROR);
} catch (NoSuchMethodException e) {
log.error(String.valueOf(e), e);
System.exit(AUTServerExitConstants.AUT_START_ERROR);
}
}
}, "Main (AUT)")); //$NON-NLS-1$
getAutThread().setDaemon(false);
getAutThread().setContextClassLoader(
ClassLoader.getSystemClassLoader());
getAutThread().start();
}
/**
* initializes the AUTServer. <br>
* 1. create communicator <br>
* 2. connect to client <br>
* 3. register shutdown hook, not yet <br>
* 4. register a AWTEventListener, not yet <br>
* 5. load the AUT <br>
* 6. send message AUTServerReady <br>
* In case of an error in step >2. send an AUTServerStateMessage with an
* error code
* @param isRcpAccessible true, if this method was called by RcpAccessor Plug-in
*/
public void start(boolean isRcpAccessible) {
m_isRcpAccessible = isRcpAccessible;
try {
IRegisterAut autReg = parseAutReg();
if (autReg == null) {
String errorMessage = "Unable to initialize connection to AUT Agent: No connection information provided."; //$NON-NLS-1$
log.error(errorMessage);
sendExitReason(errorMessage,
AUTServerExitConstants.EXIT_MISSING_AGENT_INFO);
}
if (!isRcpAccessible && !isAgentSet()) {
loadAUT(); // create a Class of the aut
}
if (m_isAgentSet || isRcpAccessible) {
setAutThread(Thread.currentThread());
}
if (m_iteCommunicator != null) {
m_iteCommunicator.send(new AUTServerStateMessage(
AUTServerStateMessage.READY));
} else {
/* calling this method from the AUT-main thread caused an
* infinite loop when waiting for the AUT-Display in SWT */
new Thread("Start AUT Server") { //$NON-NLS-1$
public void run() {
AUTStartCommand startCommand = new AUTStartCommand();
startCommand.setMessage(new AUTStartMessage());
startCommand.execute();
}
} .start();
}
// Keep the thread (and therefore the JVM) alive until the AUT thread is started.
while ((getAutThread() == null || !getAutThread().isAlive())
&& !isAutRunning()) {
try {
Thread.sleep(TimingConstantsServer.POLLING_DELAY_AUT_START);
} catch (InterruptedException e) { /* Do nothing */ }
}
if (autReg != null) {
registerAutinAgent(autReg);
}
} catch (IllegalArgumentException iae) {
log.error("Exception in start()", iae); //$NON-NLS-1$
System.exit(AUTServerExitConstants.EXIT_INVALID_ARGS);
} catch (CommunicationException ce) {
log.error("Exception in start()", ce); //$NON-NLS-1$
System.exit(AUTServerExitConstants.EXIT_COMMUNICATION_ERROR);
} catch (SecurityException se) {
log.error("Exception in start()", se); //$NON-NLS-1$
System.exit(AUTServerExitConstants
.EXIT_SECURITY_VIOLATION_REFLECTION);
} catch (ClassNotFoundException cnfe) {
sendExitReason(cnfe, AUTServerStateMessage.AUT_NOT_FOUND);
System.exit(AUTServerExitConstants.EXIT_AUT_NOT_FOUND);
} catch (NoSuchMethodException nsme) {
sendExitReason(nsme, AUTServerStateMessage.MAIN_METHOD_NOT_FOUND);
System.exit(AUTServerExitConstants.EXIT_AUT_NOT_FOUND);
} catch (UnsupportedClassVersionError ucve) {
sendExitReason(ucve,
AUTServerStateMessage.EXIT_AUT_WRONG_CLASS_VERSION);
System.exit(AUTServerExitConstants.EXIT_AUT_WRONG_CLASS_VERSION);
} catch (JBVersionException ve) {
sendExitReason(ve,
AUTServerStateMessage.EXIT_AUT_WRONG_CLASS_VERSION);
System.exit(AUTServerExitConstants.EXIT_UNKNOWN_ITE_CLIENT);
}
}
/**
* Register AUT at Agent
* @param autReg the information needed to register
* @throws JBVersionException
*/
protected void registerAutinAgent(IRegisterAut autReg)
throws JBVersionException {
try {
autReg.register();
} catch (IOException ioe) {
log.error("Exception during AUT registration", ioe); //$NON-NLS-1$
System.exit(AUTServerExitConstants.AUT_START_ERROR);
}
}
/**
*
* @return an object capable of registering the AUT managed by this server,
* or <code>null</code> if no such object could be created from the
* server's current properties.
*/
protected IRegisterAut parseAutReg() {
if (m_autAgentHost != null
&& m_autAgentPort != null && m_autID != null) {
try {
int autAgentPort = Integer.parseInt(m_autAgentPort);
InetSocketAddress agentAddr =
new InetSocketAddress(m_autAgentHost, autAgentPort);
AutIdentifier autIdentifier = new AutIdentifier(m_autID);
return new AgentRegisterAut(agentAddr, autIdentifier);
} catch (NumberFormatException nfe) {
log.warn("Unable to parse port number for AUT-Agent. Continuing as if no Aut Agent information was provided.", nfe); //$NON-NLS-1$
}
}
return null;
}
/**
* Initializes the communication between:
* <ul>
* <li>Client and AUT</li>
* <li>AUT Agent and AUT</li>
* </ul>
*
* @param clientHostName Host name to use for connecting to the client.
* @param clientPort Port number to use for connecting to the client.
* @param fragments Key: path to fragment jar. Value: fragment name
*
* @throws UnknownHostException
* if no IP address can be found for <code>clientHostName</code>
* .
*/
public void initClientCommunication(String clientHostName, int clientPort,
Map<String, String> fragments)
throws UnknownHostException {
if (!fragments.isEmpty()) {
m_errors = loadExtensions(fragments);
}
if (log.isDebugEnabled()) {
log.debug("initializing communication"); //$NON-NLS-1$
}
// create communicators
InetAddress clientAddress =
InetAddress.getByName(clientHostName);
createCommunicator(new InetSocketAddress(clientAddress, clientPort));
connectToITE();
}
/**
* Uses the class loader of the AUT server to load the extensions which are
* in the map
*
* @param fragments Key: path to fragment jar. Value: fragment name
* @return List containing error messages. A message should contain the name
* of the fragment which was affected by the error and therefore not
* loaded
*/
protected List<String> loadExtensions(Map<String, String> fragments) {
Map<URL, String> jars = new HashMap<URL, String>();
ArrayList<String> errors = new ArrayList<String>();
for (String classpath : fragments.keySet()) {
try {
String[] path = classpath
.split(AutServerLauncher.PATH_SEPARATOR);
// Splitting up the classpath of each fragment because it might
// contain several jars
for (String p : path) {
URL url = new File(p).toURI().toURL();
jars.put(url, fragments.get(classpath));
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
URL[] urls = jars.keySet().toArray(new URL[jars.keySet().size()]);
List<URL> notLoaded = addURLsToClassloader(urls);
for (URL url : notLoaded) {
errors.add(jars.get(url));
}
String path = AdapterFactoryRegistry.EXT_ADAPTER_PACKAGE_NAME.replace(
'.', '/');
List<URL> extensionsFactories = null;
try {
extensionsFactories = Collections.list(this.getClass()
.getClassLoader().getResources(path));
path = AdapterFactoryRegistry.ADAPTER_PACKAGE_NAME
.replace('.', '/');
extensionsFactories.addAll(Collections.list(this.getClass()
.getClassLoader().getResources(path)));
} catch (IOException e) {
log.error("Loading classloader resources failed: " + e); //$NON-NLS-1$
}
for (URL url : extensionsFactories) {
try {
List<Class> classes = ClassPathHacker.findClassesInJar(url,
AdapterFactoryRegistry.EXT_ADAPTER_PACKAGE_NAME,
this.getClass().getClassLoader());
classes.addAll(ClassPathHacker.findClassesInJar(url,
AdapterFactoryRegistry.ADAPTER_PACKAGE_NAME,
this.getClass().getClassLoader()));
for (Class<?> c : classes) {
if (IAdapterFactory.class.isAssignableFrom(c)) {
IAdapterFactory fac = (IAdapterFactory) c.newInstance();
if (!AdapterFactoryRegistry.getInstance()
.isRegistered(fac)) {
AdapterFactoryRegistry.getInstance()
.registerFactory(fac);
m_warnings.add("Loaded \"" + jars.get(new URL(url.getPath().split("!")[0])) + "\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
}
} catch (IOException e) {
handleException(jars, errors, url, e);
} catch (ClassNotFoundException e) {
handleException(jars, errors, url, e);
} catch (IllegalAccessException e) {
handleException(jars, errors, url, e);
} catch (InstantiationException e) {
handleException(jars, errors, url, e);
} catch (NoClassDefFoundError e) {
handleException(jars, errors, url, e);
}
}
return errors;
}
/**
* @param jars
* the JARs involved within the extension context
* @param errors
* a modifiable list of errors
* @param url
* the URL that's been used to load the JAR for
* @param t
* the throwable that occurred
*/
private void handleException(Map<URL, String> jars, List<String> errors,
URL url, Throwable t) {
try {
String error = "Could not load \"" + jars.remove(new URL(url.getPath().split("!")[0])) + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
errors.add(error);
log.error(error);
} catch (MalformedURLException e1) {
log.error("Creating error message failed: " + e1); //$NON-NLS-1$
}
log.error("Loading class failed: " + t); //$NON-NLS-1$
}
/**
* Adds the given URLs to the AUT Server class loader
*
* @param urls
* the URLs to add
* @return list of URLs which could not be added
*/
private List<URL> addURLsToClassloader(URL[] urls) {
List<URL> notLoaded = new ArrayList<URL>();
for (URL u : urls) {
try {
ClassPathHacker.addURL(u, this.getClass().getClassLoader());
} catch (IOException e) {
log.error("Could not add url: " + e); //$NON-NLS-1$
notLoaded.add(u);
} catch (SecurityException e) {
log.error("Could not add url: " + e); //$NON-NLS-1$
notLoaded.add(u);
} catch (IllegalArgumentException e) {
log.error("Could not add url: " + e); //$NON-NLS-1$
notLoaded.add(u);
} catch (NoSuchMethodException e) {
log.error("Could not add url: " + e); //$NON-NLS-1$
notLoaded.add(u);
} catch (IllegalAccessException e) {
log.error("Could not add url: " + e); //$NON-NLS-1$
notLoaded.add(u);
} catch (InvocationTargetException e) {
log.error("Could not add url: " + e); //$NON-NLS-1$
notLoaded.add(u);
} catch (NoClassDefFoundError e) {
log.error("Could not add url: " + e); //$NON-NLS-1$
notLoaded.add(u);
}
}
return notLoaded;
}
/**
* connect the ITE (integrated testing environment)
*/
protected void connectToITE() {
try {
m_iteCommunicator.run();
} catch (SecurityException se) {
log.error("Exception in start()", se); //$NON-NLS-1$
System.exit(AUTServerExitConstants
.EXIT_SECURITY_VIOLATION_COMMUNICATION);
} catch (JBVersionException e) {
log.error(e.toString());
}
}
/**
* sends an AUTServerStateMessage via Communicator if an error occurs
* @param e caught exception
* @param exitCode the exit code for the caught exception
*/
protected void sendExitReason(Throwable e, int exitCode) {
log.error("Exception in start()", e); //$NON-NLS-1$
sendExitReason(e.getMessage(), exitCode);
}
/**
* sends an AUTServerStateMessage via Communicator if an error occurs
* @param errorMessage Detailed error text for the sent message.
* @param exitCode the exit code for the error that occurred.
*/
protected void sendExitReason(String errorMessage, int exitCode) {
try {
m_iteCommunicator.send(new AUTServerStateMessage(
exitCode, errorMessage));
} catch (CommunicationException ce) {
log.error("Exception in start()", ce); //$NON-NLS-1$
}
}
/**
* Creates a communicator to the client
* @param clientAddress the clientAdress
*/
private void createCommunicator(InetSocketAddress clientAddress) {
m_iteCommunicator = createComm(clientAddress);
m_iteCommunicator.addCommunicationErrorListener(
m_communicationListener);
}
/**
* Creates a communicator to the client
* @param clientAddress the clientAdress
* @return A Communicator
*/
protected Communicator createComm(InetSocketAddress clientAddress) {
return new Communicator(clientAddress.getAddress(),
clientAddress.getPort(),
Thread.currentThread().getContextClassLoader());
}
/**
* Initializes communication between the receiver and the AUT Agent at the
* given address.
*
* @param agentAddress
* The address of the waiting AUT Agent.
* @param agentPort
* The port on which the AUT Agent is listening.
* @throws SecurityException
* if the security manager does not allow connections.
* @throws JBVersionException
* in case of version error between AUT Agent and AUT Server.
*/
public void initAutAgentCommunicator(
InetAddress agentAddress, int agentPort)
throws SecurityException, JBVersionException {
m_autAgentCommunicator = new Communicator(agentAddress, agentPort,
Thread.currentThread().getContextClassLoader());
m_autAgentCommunicator.addCommunicationErrorListener(
m_serverCommunicationListener);
m_autAgentCommunicator.run();
}
/**
* Starts the AUTs Toolkit event thread - so far only required for Swing / AWT.
* Subclasses may override!
*/
protected void startToolkitThread() {
// default is an empty implementation
}
/**
* Installs the component handler and the focus tracker. This hook may not
* be necessary in all toolkits. Subclasses may override!
*/
protected void addToolkitEventListeners() {
// default is an empty implementation
}
/**
* Adds the EventListener to the AUT<br>
* <b> Important:</b> First start the EventQueue-Thread!
*/
public void addToolKitEventListenerToAUT() {
final ClassLoader oldCL = Thread.currentThread()
.getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass()
.getClassLoader());
// install the component handler and the focus tracker
addToolkitEventListeners();
Thread.currentThread().setContextClassLoader(oldCL);
}
/**
* loads the AUT, does not instantiate the autMainClass nor invoke the main
* method, just sets the member variable autMainClass and autMainMethod
* @throws SecurityException thrown from the security manager
* @throws ClassNotFoundException thrown by Class.forName()
* @throws NoSuchMethodException thrown if no main method exists in m_autMainClass
* @throws UnsupportedClassVersionError thrown if class files are generated with unsupported jre
*/
private void loadAUT() throws SecurityException, ClassNotFoundException,
NoSuchMethodException, UnsupportedClassVersionError {
if (log.isInfoEnabled()) {
log.info("loading the AUT"); //$NON-NLS-1$
}
m_autMainClass = ClassLoader.getSystemClassLoader()
.loadClass(m_autMainClassName);
m_autMainMethod = m_autMainClass.getMethod("main", //$NON-NLS-1$
new Class[] { m_autArgs.getClass() });
// make the main method accessible
m_autMainMethod.setAccessible(true);
int mods = m_autMainMethod.getModifiers();
if (m_autMainMethod.getReturnType() != void.class
|| !Modifier.isStatic(mods)
|| !Modifier.isPublic(mods)) {
m_autMainMethod = null;
throw new NoSuchMethodException(
"no public static main"); //$NON-NLS-1$
}
}
/**
* Invokes the main method of the AUT (stored in m_autMainMethod).
*
* @throws ExceptionInInitializerError from call to invoke(), see Method.invoke()
* @throws InvocationTargetException from call to invoke(), see Method.invoke()
* @throws NoSuchMethodException if the main method could not be found during starting the AUTServer
*/
public void invokeAUT() throws ExceptionInInitializerError,
InvocationTargetException, NoSuchMethodException {
if (m_autMainMethod == null) {
log.error("the main method of the AUT could not be found!"); //$NON-NLS-1$
throw new NoSuchMethodException("no public static main in AUT"); //$NON-NLS-1$
}
try {
if (log.isDebugEnabled()) {
log.debug("invoking main method of the AUT"); //$NON-NLS-1$
}
m_isAutRunning = true;
// invoke a static method
m_autMainMethod.invoke(null, new Object[] {m_autArgs});
} catch (IllegalArgumentException iae) {
m_isAutRunning = false;
log.error(iae.getLocalizedMessage(), iae);
} catch (IllegalAccessException iae) {
m_isAutRunning = false;
log.error(iae.getLocalizedMessage(), iae);
} catch (NullPointerException npe) {
m_isAutRunning = false;
log.error(npe.getLocalizedMessage(), npe);
} catch (RuntimeException re) {
m_isAutRunning = false;
log.error("unexpected exception thrown by AUT: ", re); //$NON-NLS-1$
throw re;
}
}
/**
* changes the mode to <code>newMode</code>
* @param newMode the new mode to change to, valid values are the constants defined in ChangeAUTModeMessage.
*/
public void setMode(int newMode) {
if (log.isInfoEnabled()) {
log.info("changing mode from " //$NON-NLS-1$
+ String.valueOf(m_mode)
+ " to " //$NON-NLS-1$
+ String.valueOf(newMode));
}
// restore mode to oldMode in case of an unknown mode
int oldMode = m_mode;
if (oldMode != newMode) {
m_mode = newMode;
switch (newMode) {
// => (remove TestAWTEventListener),
// install a MappingAWTEventListener
case ChangeAUTModeMessage.AGENT_OBJECT_MAPPING:
setMappingModeAgent(true);
case ChangeAUTModeMessage.OBJECT_MAPPING:
if (newMode == ChangeAUTModeMessage.OBJECT_MAPPING) {
setMappingModeAgent(false);
}
removeToolkitEventListener(m_mappingListener);
removeToolkitEventListener(m_recordListener);
removeToolkitEventListener(m_checkListener);
refreshMode();
addToolkitEventListener(m_mappingListener);
break;
case ChangeAUTModeMessage.RECORD_MODE:
setMappingModeAgent(false);
removeToolkitEventListener(m_mappingListener);
removeToolkitEventListener(m_recordListener);
removeToolkitEventListener(m_checkListener);
m_mappingListener.cleanUp();
addToolkitEventListener(m_recordListener);
if (oldMode != ChangeAUTModeMessage.CHECK_MODE) {
setObservTimestamp(0);
}
break;
case ChangeAUTModeMessage.CHECK_MODE:
setMappingModeAgent(false);
removeToolkitEventListener(m_mappingListener);
removeToolkitEventListener(m_recordListener);
removeToolkitEventListener(m_checkListener);
m_mappingListener.cleanUp();
addToolkitEventListener(m_checkListener);
break;
case ChangeAUTModeMessage.TESTING:
setMappingModeAgent(false);
// => remove MappingAWTEventListener
removeToolkitEventListener(m_mappingListener);
removeToolkitEventListener(m_recordListener);
removeToolkitEventListener(m_checkListener);
m_recordListener.cleanUp();
m_checkListener.cleanUp();
break;
default:
log.error("unkown mode: " //$NON-NLS-1$
+ String.valueOf(newMode));
m_mode = oldMode;
}
}
}
/**
* @param agentMapping true if the agent is mapping
*/
private void setMappingModeAgent(boolean agentMapping) {
m_isAgentObjectMapping = agentMapping;
}
/**
* refreshes AUT
*/
public void refreshMode() {
m_mappingListener.cleanUp();
m_recordListener.cleanUp();
m_checkListener.cleanUp();
}
/**
* Adds the given listener to the AWTEventQueue. <br>
* In case of a security exception this method will closing the VM.
* @param listener the listener to add, can be null
*/
protected abstract void addToolkitEventListener(BaseAUTListener listener);
/**
* Removes the given listener from the AWTEventQueue. <br>
* In case of a security exception this method will closing the VM.
*
* @param listener the listener to remove, can be null
*/
protected abstract void removeToolkitEventListener(
BaseAUTListener listener);
/**
* @return the mode the AUTserver is in, see also <code>setMode()</code>.
*/
public int getMode() {
return m_mode;
}
/**
* highlight a component sent to server by client
* @param comp ComponentIdentifier
* @return boolean successful?
*/
public boolean highlightComponent(IComponentIdentifier comp) {
return m_mappingListener.highlightComponent(comp);
}
/**
* highlight a component sent to server by client
*/
public void updateHighLighter() {
m_mappingListener.update();
}
/**
* @throws ExceptionInInitializerError Error
* @throws InvocationTargetException Error
* @throws NoSuchMethodException Error
*/
protected abstract void startTasks()
throws ExceptionInInitializerError,
InvocationTargetException,
NoSuchMethodException;
/**
* The listener listening for event from the communicator.
* @author BREDEX GmbH
* @created 05.08.2004
*
*/
private abstract class AbstractCommunicationListener
implements ICommunicationErrorListener {
/**
* {@inheritDoc}
*/
public void acceptingFailed(int port) {
log.debug("acceptingFailed() called although " + //$NON-NLS-1$
"this is a client"); //$NON-NLS-1$
terminate();
}
/**
* {@inheritDoc}
*/
public void connectingFailed(InetAddress inetAddress, int port) {
String message = StringConstants.EMPTY;
try {
message = "connecting to " //$NON-NLS-1$
+ inetAddress.getHostName()
+ ":" + String.valueOf(port) //$NON-NLS-1$
+ " failed()"; //$NON-NLS-1$
log.error(message);
} catch (SecurityException se) {
log.error("security violation during getting the " //$NON-NLS-1$
+ " host name from ip address " //$NON-NLS-1$
+ "in connectingFailed()"); //$NON-NLS-1$
} finally {
terminate();
}
}
/**
* {@inheritDoc}
*/
public void connectionGained(InetAddress inetAddress, int port) {
if (log.isInfoEnabled()) {
String message;
try {
message = "connected to " //$NON-NLS-1$
+ inetAddress.getHostName()
+ ":" + String.valueOf(port); //$NON-NLS-1$
} catch (SecurityException se) {
log.debug("security violation during getting" //$NON-NLS-1$
+ " the host name from ip address " //$NON-NLS-1$
+ "in connectingGained()"); //$NON-NLS-1$
message = " connected"; //$NON-NLS-1$
}
log.info(message);
}
}
/**
* {@inheritDoc}
*/
public void sendFailed(Message message) {
log.error("sending message " + message.toString() + "failed"); //$NON-NLS-1$ //$NON-NLS-2$
terminate();
}
/**
* {@inheritDoc}
*/
public void shutDown() {
if (log.isInfoEnabled()) {
log.info("connection closed"); //$NON-NLS-1$
log.info("exiting with " + String //$NON-NLS-1$
.valueOf(AUTServerExitConstants.EXIT_COMMUNICATION_ERROR));
}
terminate();
}
/**
* Handle terminated connection.
*/
protected abstract void terminate();
}
/**
* Listener for events that occur during communication with an
* AUT Agent.
*
* @author BREDEX GmbH
*/
private class AgentCommunicationListener
extends AbstractCommunicationListener {
/**
* {@inheritDoc}
*/
protected void terminate() {
try {
shutdown();
} finally {
System.exit(AUTServerExitConstants.EXIT_COMMUNICATION_ERROR);
}
}
}
/**
* Listener for events that occur during communication with a client.
*
* @author BREDEX GmbH
*/
private class ClientCommunicationListener
extends AbstractCommunicationListener {
/**
* {@inheritDoc}
*/
protected void terminate() {
ChangeAUTModeMessage message = new ChangeAUTModeMessage();
message.setMode(ChangeAUTModeMessage.TESTING);
ChangeAUTModeCommand changeModeCmd = new ChangeAUTModeCommand();
changeModeCmd.setMessage(message);
changeModeCmd.execute();
if (m_iteCommunicator != null) {
m_iteCommunicator.close();
}
}
}
/** @return the autThread **/
protected Thread getAutThread() {
return m_autThread;
}
/**
* @return Returns true if the AUT is running, false otherwise.
*/
public boolean isAutRunning() {
return m_isAutRunning;
}
/**
* @return An IRobot instance
*/
public abstract IRobot getRobot();
/**
* @param ci
* the component identifier
* @param timeout
* the timeout
* @return the found component
* @throws IllegalArgumentException
* if error occurred
* @throws ComponentNotFoundException
* if component could not found in compHierarchy
*/
public abstract Object findComponent(IComponentIdentifier ci,
int timeout) throws ComponentNotFoundException,
IllegalArgumentException;
/**
* @param ci
* the component identifier
* @param timeout
* the timeout
* @return the found component
* @throws IllegalArgumentException
* if error occurred
* @throws ComponentNotFoundException
* if component could not found in compHierarchy
*/
public abstract boolean isComponentDisappeared(IComponentIdentifier ci,
int timeout) throws ComponentNotFoundException,
IllegalArgumentException;
/**
* Starts an Inspector that allows data for the next component clicked to
* be sent to the client.
*
*/
public final void startInspector() {
IAutListenerAppender[] inspectorAppenders = m_inspectorListenerAppenders
.toArray(new IAutListenerAppender[m_inspectorListenerAppenders
.size()]);
for (int i = 0; i < inspectorAppenders.length; i++) {
inspectorAppenders[i].addAutListener();
}
}
/**
* Adds the given appender to the list of appenders that will be called
* when the Inspector is activated.
*
* @param appender The appender to add.
*/
public final void addInspectorListenerAppender(
IAutListenerAppender appender) {
m_inspectorListenerAppenders.add(appender);
}
/**
* <HR NOSHADE><CENTER><FONT color="#FF0000"><b>ONLY TO USE FOR SWT-JUNIT TESTS<br>
* AND<br>FOR "RcpAccessor" PLUG-IN !!!</b></FONT></CENTER><HR NOSHADE>
* @param swtAutServerName the swtAutServerName to set
* @return an single instance of the autSrever
*/
public static AUTServer getInstance(String swtAutServerName) {
autServerName = swtAutServerName;
return getInstance();
}
/**
* @return the true, if AUT server runs as plugin in RCP-AUT
*/
public boolean isRcpAccessible() {
return m_isRcpAccessible;
}
/**
* @return true, if AUT server got started via Java Agent
*/
public boolean isAgentSet() {
return m_isAgentSet;
}
/**
* @return the isObservingDialogOpen
*/
public boolean isObservingDialogOpen() {
return m_isObservingDialogOpen;
}
/**
* @param isObservingDialogOpen the isObservingDialogOpen to set
*/
public void setObservingDialogOpen(boolean isObservingDialogOpen) {
m_isObservingDialogOpen = isObservingDialogOpen;
}
/**
* @return timestamp of last recorded action
*/
public long getObservTimestamp() {
return m_observTimestamp;
}
/**
*
* @return List of Strings which contains the error massages from the last time
* extensions were loaded
*/
public List<String> getErrors() {
return m_errors;
}
/**
*
* @return List of Strings which contains the warning massages from the last time
* extensions were loaded
*/
public List<String> getWarnings() {
return m_warnings;
}
/**
* @param timestamp the timestamp of last recorded action
*/
public void setObservTimestamp(long timestamp) {
m_observTimestamp = timestamp;
}
/**
* @param autThread the autThread to set
*/
private void setAutThread(Thread autThread) {
m_autThread = autThread;
}
/**
* @param singletonInstance the instance to set
*/
protected static void setInstance(AUTServer singletonInstance) {
AUTServer.instance = singletonInstance;
}
/**
* call back method to tear down AUTServer in case of termination e.g.
* communication errors, subclasses may override
*/
public void shutdown() {
// empty
}
/**
* @param autAgentHost the autAgentHost to set
*/
public void setAutAgentHost(String autAgentHost) {
m_autAgentHost = autAgentHost;
}
/**
* @param autAgentPort the autAgentPort to set
*/
public void setAutAgentPort(String autAgentPort) {
m_autAgentPort = autAgentPort;
}
/**
* @param autID the autName to set
*/
public void setAutID(String autID) {
m_autID = autID;
}
/**
* returns the class loader
* @return the external class loader
*/
public ClassLoader getExternalLoader() {
return m_externalLoader;
}
/**
* @return the reference to the component at which an error occured
*/
public WeakReference<IComponent> getErrorComponent() {
return m_errorComponent;
}
/**
* @param errorComponent reference to the component at which an error occured
*/
public void setErrorComponent(WeakReference<IComponent> errorComponent) {
m_errorComponent = errorComponent;
}
}