blob: 390acc3e1e04186f409a36b5a52214a5ed73fc54 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2011 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.app.autrun;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.Parser;
import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.adaptor.EclipseStarter;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.jubula.app.autrun.i18n.Messages;
import org.eclipse.jubula.tools.internal.constants.AutConfigConstants;
import org.eclipse.jubula.tools.internal.constants.EnvConstants;
import org.eclipse.jubula.tools.internal.constants.StringConstants;
import org.eclipse.jubula.tools.internal.registration.AutIdentifier;
import org.eclipse.jubula.tools.internal.utils.IsAliveThread;
import org.eclipse.jubula.tools.internal.utils.TimeUtil;
import org.eclipse.jubula.version.Vn;
import org.eclipse.osgi.util.NLS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Starts an AUT and registers the AUT with an AUT Agent. In order to terminate
* at the right time (not too early, not too late) this application assumes
* that the following JVM parameters (or equivalent) are used:<ul>
* <li>osgi.noShutdown=true</li>
* <li>eclipse.jobs.daemon=true</li>
* </ul>
* These parameters are required because the original application was designed
* to run outside of an OSGi context, i.e. the application should end only
* when no non-daemon threads are active.
*
* @author BREDEX GmbH
* @created Dec 9, 2009
*/
public class AutRunApplication implements IApplication {
/** the logger */
private static final Logger LOG =
LoggerFactory.getLogger(AutRunApplication.class);
/**
* <code>LAUNCHER_NAME</code>
*/
private static final String LAUNCHER_NAME = "autrun"; //$NON-NLS-1$
/** <code>TOOLKIT_RCP</code> */
private static final String TK_RCP = "rcp"; //$NON-NLS-1$
/** <code>TK_SWT</code> */
private static final String TK_SWT = "swt"; //$NON-NLS-1$
/** <code>TK_SWING</code> */
private static final String TK_SWING = "swing"; //$NON-NLS-1$
/** <code>TK_JAVAFX</code> */
private static final String TK_JAVAFX = "javafx"; //$NON-NLS-1$
/** <code>DEFAULT_NAME_TECHNICAL_COMPONENTS</code> */
private static final boolean DEFAULT_NAME_TECHNICAL_COMPONENTS = true;
// - Command line options - Start //
/** port number for the Aut Agent with which to register */
private static final String OPT_AUT_AGENT_PORT = "p"; //$NON-NLS-1$
/** port number for the Aut Agent with which to register */
private static final String OPT_AUT_AGENT_PORT_LONG = "autagentport"; //$NON-NLS-1$
/** hostname for the Aut Agent with which to register */
private static final String OPT_AUT_AGENT_HOST = "a"; //$NON-NLS-1$
/** hostname for the Aut Agent with which to register */
private static final String OPT_AUT_AGENT_HOST_LONG = "autagenthost"; //$NON-NLS-1$
/** help option */
private static final String OPT_HELP = "h"; //$NON-NLS-1$
/** hostname for the Aut Agent with which to register */
private static final String OPT_HELP_LONG = "help"; //$NON-NLS-1$
/** name of the AUT to register */
private static final String OPT_AUT_ID = "i"; //$NON-NLS-1$
/** name of the AUT to register */
private static final String OPT_AUT_ID_LONG = "autid"; //$NON-NLS-1$
/** flag for name generation for certain technical components */
private static final String OPT_NAME_TECHNICAL_COMPONENTS = "g"; //$NON-NLS-1$
/** flag for name generation for certain technical components */
private static final String OPT_NAME_TECHNICAL_COMPONENTS_LONG = "generatenames"; //$NON-NLS-1$
/** keyboard layout */
private static final String OPT_KEYBOARD_LAYOUT = "k"; //$NON-NLS-1$
/** keyboard layout */
private static final String OPT_KEYBOARD_LAYOUT_LONG = "kblayout"; //$NON-NLS-1$
/** AUT working directory */
private static final String OPT_WORKING_DIR = "w"; //$NON-NLS-1$
/** AUT working directory */
private static final String OPT_WORKING_DIR_LONG = "workingdir"; //$NON-NLS-1$
/** executable file used to start the AUT */
private static final String OPT_EXECUTABLE = "e"; //$NON-NLS-1$
/** executable file used to start the AUT */
private static final String OPT_EXECUTABLE_LONG = "exec"; //$NON-NLS-1$
/** AUT agent host name */
private static final String HOSTNAME = "hostname"; //$NON-NLS-1$
/** AUT agent port */
private static final String PORT = "port"; //$NON-NLS-1$
/** AUT id */
private static final String ID = "id"; //$NON-NLS-1$
/** technical components */
private static final String TRUE_FALSE = "true / false"; //$NON-NLS-1$
/** AUT keyboard layout */
private static final String LOCALE = "locale"; //$NON-NLS-1$
/** AUT working directory */
private static final String DIRECTORY = "directory"; //$NON-NLS-1$
/** AUT options */
private static final String COMMAND = "command"; //$NON-NLS-1$
/** swing class prefix */
private static final String SWING_AUT_TOOLKIT_CLASS_PREFIX = "Swing"; //$NON-NLS-1$
/** SWT class prefix */
private static final String SWT_AUT_TOOLKIT_CLASS_PREFIX = "Swt"; //$NON-NLS-1$
/** RCP class prefix */
private static final String RCP_AUT_TOOLKIT_CLASS_PREFIX = "Rcp"; //$NON-NLS-1$
/** JavaFX class prefix */
private static final String JAVAFX_AUT_TOOLKIT_CLASS_PREFIX = "JavaFX"; //$NON-NLS-1$
// - Command line options - End //
/**
* prints help options
* @param pe a parse Exception - may also be null
*/
private static void printHelp(ParseException pe) {
System.out.println(Vn.getDefault().getVersion());
HelpFormatter formatter = new HelpFormatter();
formatter.setOptionComparator(new OptionComparator());
if (pe != null) {
formatter.printHelp(
LAUNCHER_NAME,
StringConstants.EMPTY,
createCmdLineOptions(),
"\n" + pe.getLocalizedMessage(), //$NON-NLS-1$
true);
} else {
formatter.printHelp(LAUNCHER_NAME,
createCmdLineOptions(), true);
}
}
/**
* Creates and returns settings for starting an AUT based on the given
* command line.
*
* @param cmdLine Provides the settings for the AUT configuration.
* @return new settings for starting an AUT.
*/
private static Map<String, String> createAutConfig(CommandLine cmdLine) {
Map<String, String> autConfig = new HashMap<String, String>();
if (cmdLine.hasOption(OPT_WORKING_DIR)) {
autConfig.put(AutConfigConstants.WORKING_DIR, cmdLine
.getOptionValue(OPT_WORKING_DIR));
} else {
autConfig.put(AutConfigConstants.WORKING_DIR, System
.getProperty("user.dir")); //$NON-NLS-1$
}
if (cmdLine.hasOption(OPT_NAME_TECHNICAL_COMPONENTS)) {
autConfig.put(AutConfigConstants.NAME_TECHNICAL_COMPONENTS, String
.valueOf(cmdLine.getOptionValue(
OPT_NAME_TECHNICAL_COMPONENTS)));
} else {
autConfig.put(AutConfigConstants.NAME_TECHNICAL_COMPONENTS,
String.valueOf(DEFAULT_NAME_TECHNICAL_COMPONENTS));
}
autConfig.put(AutConfigConstants.EXECUTABLE, cmdLine
.getOptionValue(OPT_EXECUTABLE));
if (cmdLine.hasOption(OPT_KEYBOARD_LAYOUT)) {
autConfig.put(AutConfigConstants.KEYBOARD_LAYOUT,
cmdLine.getOptionValue(OPT_KEYBOARD_LAYOUT));
}
String[] autArguments = cmdLine.getOptionValues(OPT_EXECUTABLE);
if (autArguments.length > 1) {
autConfig.put(AutConfigConstants.AUT_RUN_AUT_ARGUMENTS, StringUtils
.join(autArguments,
AutConfigConstants.AUT_RUN_AUT_ARGUMENTS_SEPARATOR_CHAR, 1,
autArguments.length));
}
return autConfig;
}
/**
* @return the command line options available when invoking the main method.
*/
private static Options createCmdLineOptions() {
Options options = new Options();
Option autAgentHostOption = new Option(OPT_AUT_AGENT_HOST, true,
NLS.bind(Messages.infoAutAgentHost, EnvConstants.LOCALHOST_ALIAS));
autAgentHostOption.setLongOpt(OPT_AUT_AGENT_HOST_LONG);
autAgentHostOption.setArgName(HOSTNAME);
options.addOption(autAgentHostOption);
Option autAgentPortOption = new Option(OPT_AUT_AGENT_PORT, true,
NLS.bind(Messages.infoAutAgentPort,
EnvConstants.AUT_AGENT_DEFAULT_PORT));
autAgentPortOption.setLongOpt(OPT_AUT_AGENT_PORT_LONG);
autAgentPortOption.setArgName(PORT);
options.addOption(autAgentPortOption);
OptionGroup autToolkitOptionGroup = new OptionGroup();
autToolkitOptionGroup.addOption(new Option(TK_SWING,
Messages.infoSwingToolkit));
autToolkitOptionGroup.addOption(new Option(TK_SWT,
Messages.infoSwtToolkit));
autToolkitOptionGroup.addOption(new Option(TK_RCP,
Messages.infoRcpToolkit));
autToolkitOptionGroup.addOption(new Option(TK_JAVAFX,
Messages.infoJavaFXToolkit));
autToolkitOptionGroup.setRequired(true);
options.addOptionGroup(autToolkitOptionGroup);
Option autIdOption = new Option(OPT_AUT_ID, true, Messages.infoAutId);
autIdOption.setLongOpt(OPT_AUT_ID_LONG);
autIdOption.setArgName(ID);
autIdOption.setRequired(true);
options.addOption(autIdOption);
Option nameTechnicalComponentsOption = new Option(
OPT_NAME_TECHNICAL_COMPONENTS, true,
Messages.infoGenerateTechnicalComponentNames);
nameTechnicalComponentsOption
.setLongOpt(OPT_NAME_TECHNICAL_COMPONENTS_LONG);
nameTechnicalComponentsOption.setArgName(TRUE_FALSE);
options.addOption(nameTechnicalComponentsOption);
Option keyboardLayoutOption = new Option(OPT_KEYBOARD_LAYOUT, true,
Messages.infoKbLayout);
keyboardLayoutOption.setLongOpt(OPT_KEYBOARD_LAYOUT_LONG);
keyboardLayoutOption.setArgName(LOCALE);
options.addOption(keyboardLayoutOption);
Option workingDirOption = new Option(OPT_WORKING_DIR, true,
Messages.infoAutWorkingDirectory);
workingDirOption.setLongOpt(OPT_WORKING_DIR_LONG);
workingDirOption.setArgName(DIRECTORY);
options.addOption(workingDirOption);
Option helpOption = new Option(OPT_HELP, false, Messages.infoHelp);
helpOption.setLongOpt(OPT_HELP_LONG);
options.addOption(helpOption);
OptionBuilder.hasArgs();
Option autExecutableFileOption = OptionBuilder.create(OPT_EXECUTABLE);
autExecutableFileOption.setDescription(Messages.infoExecutableFile);
autExecutableFileOption.setLongOpt(OPT_EXECUTABLE_LONG);
autExecutableFileOption.setRequired(true);
autExecutableFileOption.setArgName(COMMAND);
options.addOption(autExecutableFileOption);
return options;
}
/**
* @author BREDEX GmbH
*/
private static final class WatchDog extends Thread {
/**
* the barrier to await
*/
private CyclicBarrier m_b;
/**
* Constructor
*
* @param name
* the name
* @param b the barrier to use
*/
private WatchDog(String name, CyclicBarrier b) {
super(name);
m_b = b;
}
/** {@inheritDoc} */
public void run() {
try {
boolean shouldShutdown;
do {
TimeUtil.delay(2500);
shouldShutdown = true;
Set<Thread> allThreads = Thread.getAllStackTraces()
.keySet();
for (Thread t : allThreads) {
if (t instanceof IsAliveThread) {
shouldShutdown = false;
break;
}
}
} while (!shouldShutdown);
} finally {
try {
m_b.await();
} catch (InterruptedException e) {
LOG.error(e.getLocalizedMessage(), e);
} catch (BrokenBarrierException e) {
LOG.error(e.getLocalizedMessage(), e);
}
}
try {
EclipseStarter.shutdown();
} catch (Exception e) {
LOG.error(e.getLocalizedMessage(), e);
}
}
}
/**
* This class implements the <code>Comparator</code> interface for comparing
* Options.
*/
private static class OptionComparator implements Comparator {
/** {@inheritDoc} */
public int compare(Object o1, Object o2) {
Option opt1 = (Option)o1;
Option opt2 = (Option)o2;
// always list -exec as last option
if (opt1.getOpt().equals(OPT_EXECUTABLE)) {
return 1;
}
if (opt2.getOpt().equals(OPT_EXECUTABLE)) {
return -1;
}
return 0;
}
}
/**
* {@inheritDoc}
*/
public Object start(final IApplicationContext context) throws Exception {
String[] args = (String[])context.getArguments().get(
IApplicationContext.APPLICATION_ARGS);
if (args == null) {
args = new String[0];
}
Options options = createCmdLineOptions();
Parser parser = new BasicParser();
CommandLine cmdLine = null;
try {
cmdLine = parser.parse(options, args, false);
if (!cmdLine.hasOption(OPT_HELP)) {
String toolkit = StringConstants.EMPTY;
if (cmdLine.hasOption(TK_SWING)) {
toolkit = SWING_AUT_TOOLKIT_CLASS_PREFIX;
} else if (cmdLine.hasOption(TK_SWT)) {
toolkit = SWT_AUT_TOOLKIT_CLASS_PREFIX;
} else if (cmdLine.hasOption(TK_RCP)) {
toolkit = RCP_AUT_TOOLKIT_CLASS_PREFIX;
} else if (cmdLine.hasOption(TK_JAVAFX)) {
toolkit = JAVAFX_AUT_TOOLKIT_CLASS_PREFIX;
}
int autAgentPort = EnvConstants.AUT_AGENT_DEFAULT_PORT;
if (cmdLine.hasOption(OPT_AUT_AGENT_PORT)) {
try {
autAgentPort = Integer.parseInt(cmdLine
.getOptionValue(OPT_AUT_AGENT_PORT));
} catch (NumberFormatException nfe) {
// use default
}
}
String autAgentHost = EnvConstants.LOCALHOST_ALIAS;
if (cmdLine.hasOption(OPT_AUT_AGENT_HOST)) {
autAgentHost = cmdLine.getOptionValue(OPT_AUT_AGENT_HOST);
}
InetSocketAddress agentAddr =
new InetSocketAddress(autAgentHost, autAgentPort);
AutIdentifier autId = new AutIdentifier(cmdLine
.getOptionValue(OPT_AUT_ID));
Map<String, String> autConfiguration = createAutConfig(cmdLine);
AutRunner runner = new AutRunner(
toolkit, autId, agentAddr, autConfiguration);
try {
runner.run();
} catch (ConnectException ce) {
LOG.info(Messages.infoConnectionToAutAgentFailed, ce);
System.err.println(Messages.infoNonAutAgentConnectionInfo);
}
} else {
printHelp(null);
}
} catch (ParseException pe) {
printHelp(pe);
} finally {
CyclicBarrier b = new CyclicBarrier(2);
Thread watchDog = new WatchDog("http://eclip.se/457600#c8", b); //$NON-NLS-1$
watchDog.setDaemon(true);
watchDog.start();
// http://eclip.se/461810: prevent application to quit too early, otherwise framework bundle lookups do not work
b.await();
}
return IApplication.EXIT_OK;
}
/**
*
* {@inheritDoc}
*/
public void stop() {
// no-op
}
}