// Copyright � 2002-2005 Canoo Engineering AG, Switzerland.
package com.canoo.webtest.extension.applet.runner;
import java.applet.Applet;
import java.awt.Frame;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URLClassLoader;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.netbeans.jemmy.JemmyProperties;
import org.netbeans.jemmy.Scenario;
import org.netbeans.jemmy.TestOut;
import com.canoo.webtest.boundary.AppletRunnerBoundary;
import com.canoo.webtest.extension.applet.AbstractAppletTag;
import com.canoo.webtest.extension.applet.AppletPluginArguments;
import com.canoo.webtest.extension.applet.AppletPluginResults;
import com.canoo.webtest.extension.applet.runner.http.HttpURLConnection;
import com.canoo.webtest.util.FileUtil;
/**
* Main runner for applet test scenarii.
* <p>If the application cannot start the scenario, the application ends with a non-zero exit value.
* <p>If the application can start the scenario, the application ends with {@link #EXIT_OK 0} and stores the result
* of the scenario in a {@link AppletPluginResults}.
* @author Denis N. Antonioli
* @author Paul King
*/
public class AppletRunner {
private static final Logger LOG = Logger.getLogger(AppletRunner.class);
private static final Class[] PARAMETER_TYPES_SCENARIO_CONSTRUCTOR = new Class[]{AppletRunner.class, Frame.class};
private static final Class[] PARAMETER_TYPES_APPLET_CONSTRUCTOR = new Class[]{};
private static final Object[] PARAMETER_APPLET_CONSTRUCTOR = new Object[]{};
public static final int EXIT_OK = 0;
public static final int EXIT_ERROR_NO_APPLET_RUNNER = -1;
public static final int EXIT_ERROR_INSTANTIATE_APPLET_RUNNER = -2;
public static final int EXIT_ERROR_DURING_RUN = -3;
public static final int EXIT_ERROR_ARGUMENT_IO = -4;
public static final int EXIT_ERROR_ARGUMENT_CLASS = -5;
private final AppletPluginArguments fAppletPluginArguments;
private final Context fAppletContext;
/**
* Entry point for the test runner.
* <p>The method <em>must</em> end with a {@link System#exit(int)} to also end the awt/swing engine.
* If the method just ends, the event queue for the gui keeps the JVM (and the applet!) running.
*/
public static void main(String args[]) {
JemmyProperties.setCurrentOutput(new TestOut(System.in,
new PrintWriter(new LoggingOutputStream(TestOut.class, LOG, Level.INFO), true),
new PrintWriter(new LoggingOutputStream(TestOut.class, LOG, Level.WARN), true)));
AppletPluginArguments appletPluginArguments = readPluginArguments(args);
HttpURLConnection.setCookies(appletPluginArguments.getCookies());
try {
new AppletRunner(appletPluginArguments).run();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
System.exit(EXIT_ERROR_DURING_RUN);
}
System.exit(EXIT_OK);
}
private static AppletPluginArguments readPluginArguments(String[] args) {
return new AppletRunnerHelper(EXIT_ERROR_ARGUMENT_CLASS, EXIT_ERROR_ARGUMENT_IO, new AppletRunnerBoundary()).readPluginArguments(args[0]);
}
public AppletRunner(AppletPluginArguments appletPluginArguments) {
fAppletPluginArguments = appletPluginArguments;
fAppletContext = new Context(new AppletPluginResults());
}
void run() throws Exception {
AbstractAppletTag appletTag = fAppletPluginArguments.getAppletTag();
AbstractAppletStub appletStub = fAppletContext.newStub(newApplet(appletTag), appletTag, fAppletPluginArguments);
final Scenario scenario = newScenario(appletStub.getRootFrame());
final ScenarioRunner jemmyRunnable = new ScenarioRunner(scenario);
internalRun(appletStub, jemmyRunnable);
}
void internalRun(AbstractAppletStub appletStub, ScenarioRunner jemmyRunnable) throws InterruptedException {
appletStub.init();
appletStub.show();
appletStub.start();
try {
jemmyRunnable.startTest();
jemmyRunnable.join();
fAppletContext.getAppletPluginResults().setReturnValue((Integer) jemmyRunnable.getResult());
if (jemmyRunnable.getException() != null) {
fAppletContext.getAppletPluginResults().setException(jemmyRunnable.getException());
LOG.error(jemmyRunnable.getException().getMessage(), jemmyRunnable.getException());
}
AppletRunnerBoundary.storeJemmyExceptionIfNeeded(jemmyRunnable.getJemmyException(), fAppletContext);
} catch (InterruptedException e) {
LOG.warn("Scenario " + fAppletPluginArguments.getScenario() + " interrupted", e);
throw e;
} finally {
appletStub.stop();
appletStub.destroy();
writeArguments(fAppletContext.getAppletPluginResults());
}
}
protected void writeArguments(AppletPluginResults apr) {
FileUtil.tryWriteObjectToFile(fAppletPluginArguments.getOutputFile(), apr, null);
}
/**
* Loads and instantiates a {@link org.netbeans.jemmy.Scenario}. The method looks on the application's classpath for
* the named test scenario. Looks also in the property {@link AppletPluginArguments#getScenarioLocation()
* scenarioLocation}, if it is set.
*
* @param rootFrame The root frame for the execution of jemmy.
* @return A newly instantiated test scenario.
*/
AbstractScenario newScenario(final Frame rootFrame) throws Exception {
ClassLoader classLoader = new URLClassLoader(fAppletPluginArguments.getScenarioLocation(), getClass().getClassLoader());
Object object = createObject(classLoader, fAppletPluginArguments.getScenario(),
PARAMETER_TYPES_SCENARIO_CONSTRUCTOR, new Object[]{this, rootFrame});
return (AbstractScenario) AppletRunnerBoundary.assertObjectHasCorrectClass(AbstractScenario.class, object,
"Parameter scenario does not select a " + AbstractScenario.class.getName() + ": ");
}
Applet newApplet(final AbstractAppletTag appletTag) throws Exception {
URLClassLoader appletLoader = AppletRunnerBoundary.tryCreateUrlClassLoader(appletTag, null);
Object object = createObject(appletLoader, appletTag.getCode(),
PARAMETER_TYPES_APPLET_CONSTRUCTOR, PARAMETER_APPLET_CONSTRUCTOR);
return (Applet) AppletRunnerBoundary.assertObjectHasCorrectClass(Applet.class, object,
"The xpath does not select an applet: ");
}
private static Object createObject(final ClassLoader classLoader, final String className, final Class[] ctorParamType, final Object[] ctorParam)
throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class aClass = Class.forName(className, true, classLoader);
Constructor constructor = aClass.getConstructor(ctorParamType);
return constructor.newInstance(ctorParam);
}
public AppletPluginArguments getAppletPluginArguments() {
return fAppletPluginArguments;
}
public Context getAppletContext() {
return fAppletContext;
}
}