<table cellpadding="1" border="1">
<tr><th>Component</th><th>Options</th></tr>
-<tr><td rowspan="5" valign="top">Harness
+<tr><td rowspan="6" valign="top">Harness
<p>suite files, harness verbosity, temp files, option variants
</td></tr>
<tr><td><u>suite files</u>: ajcTest-compliant .txt or .xml files are accepted.
or <tt>-loud</tt> is used.)
</td></tr>
+
+ <tr><td rowspan="2" valign="top">JavaRun
+ <p>Options and forking</td>
+ <td><u>options</u>: options specified in the test are passed
+ to the main method as they would be on the command-line.
+ No options passed to the harness are passed through to
+ the main class.
+ </td></tr>
+ <tr><td><u>forking</u>:
+ Forking is useful to run in a different version of Java
+ than can be supported by the harness (i.e., some 1.1 flavor);
+ it's very time-consuming otherwise.
+ Currently forking is only controllable through system properties
+ of the invoking vm (defined in JavaRun):
+ <ul>
+ <li><u>javarun.fork</u>: anything to run in a new vm.
+ </li>
+ <li><u>javarun.java</u>: path to the java executable to run
+ (suffix included). If not supplied, the harness tries to
+ find the java that invoked the harness.
+ </li>
+ <li><u>javarun.java.home</u>: the value of the JAVA_HOME
+ environment variable, if it needs to be set.
+ </li>
+ <li><u>javarun.classpath</u>: this is prepended to the
+ run classpath. Multiple entries must be separated by
+ the system-dependent path separator.
+ </li>
+ </ul>
+ </td></tr>
</table>
<p>
Following are some sample configurations:
define the environment variable <code>harness.libdir</code> - e.g.,
<pre>
$ cd aspectj/tests
- $ java -Dharness.libdir=../modules/lib/test -jar eajctesting.jar ajcTests.xml
+ $ java -Dharness.libdir=../modules/lib/test ...
+</pre>
+
+<p><u>Forking:</u>:
+The harness must be run in a compiler-compatible VM, and the
+compiler steps run in-process.
+However, the java steps can be run in forked mode, which is useful
+when compiling for a VM which can't run the compiler.
+Here's how you would run the harness to compile against a
+specific version of 1.1:
+<pre>
+ java -Djavarun.java=d:\jdk1.1.8\bin\java.exe \
+ -Djavarun.bootclasspath=d:\jdk1.1.8\lib\classes.zip \
+ -Djavarun.java.home=d:\jdk1.1.8 \
+ -Djavarun.fork=true \
+ -jar ../aj-build/jars/testing-drivers-all.jar \
+ ajcTests.xml -logFail
+</pre>
+
+Here CompilerRun would add the bootclasspath as such when compiling.
+JavaRun would fork using the 1.1 vm and prepend the bootclasspath
+to the classpath, with an effect like these commands
+(ignoring the line splitting in the classpath):
+<pre>
+ set JAVA_HOME=d:\jdk1.1.8
+ d:\jdk1.1.8\bin\java.exe \
+ -classpath "d:\jdk1.1.8\lib\classes.zip;
+ d:\aspectj-src\lib\test\testing-client.jar;
+ d:\aspectj-src\lib\test\aspectjrt.jar;
+ c:\TEMP\sandbox7wers\classes"
+ {mainClass} {option..}
</pre>
-<p>
</body>
-</html>
\ No newline at end of file
+</html>
package org.aspectj.testing.harness.bridge;
-import java.io.File;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.util.Arrays;
-import java.util.List;
-
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.testing.Tester;
import org.aspectj.util.FileUtil;
import org.aspectj.util.LangUtil;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Run a class in this VM using reflection.
+ * Forked mode supported, but through system properties:
+ * - javarun.fork: anything to enable forking
+ * - javarun.java: path to java executable (optional)
+ * - javarun.java.home: JAVA_HOME for java (optional)
+ * (normally requires javarun.java)
+ * - javarun.classpath: a prefix to the run classpath (optional)
*/
public class JavaRun implements IAjcRun {
+ public static String FORK_KEY = "javarun.fork";
+ public static String JAVA_KEY = "javarun.java";
+ public static String JAVA_HOME_KEY = "javarun.java.home";
+ public static String BOOTCLASSPATH_KEY = "javarun.bootclasspath";
+ private static final boolean FORK;
+ private static final String JAVA;
+ private static final String JAVA_HOME;
+ static final String BOOTCLASSPATH;
+ static {
+ FORK = (null != getProperty(FORK_KEY));
+ JAVA = getProperty(JAVA_KEY);
+ JAVA_HOME = getProperty(JAVA_HOME_KEY);
+ BOOTCLASSPATH = getProperty(BOOTCLASSPATH_KEY);
+ }
+ private static String getProperty(String key) {
+ try {
+ return System.getProperty(key);
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
+ private static void appendClasspath(StringBuffer cp, File[] libs, File[] dirs) {
+ if (!LangUtil.isEmpty(BOOTCLASSPATH)) {
+ cp.append(BOOTCLASSPATH);
+ cp.append(File.pathSeparator);
+ }
+ for (int i = 0; i < dirs.length; i++) {
+ cp.append(dirs[i].getAbsolutePath());
+ cp.append(File.pathSeparator);
+ }
+ for (int i = 0; i < libs.length; i++) {
+ cp.append(libs[i].getAbsolutePath());
+ cp.append(File.pathSeparator);
+ }
+ // ok to have trailing path separator I guess...
+ }
+
Spec spec;
private Sandbox sandbox;
InvocationTargetException,
ClassNotFoundException,
NoSuchMethodException {
-
boolean completedNormally = false;
- Class targetClass = null;
if (!LangUtil.isEmpty(spec.dirChanges)) {
MessageUtil.info(status, "XXX dirChanges not implemented in JavaRun");
}
- TestClassLoader loader = null;
try {
final boolean readable = true;
File[] libs = sandbox.getClasspathJars(readable, this);
- URL[] urls = FileUtil.getFileURLs(libs);
File[] dirs = sandbox.getClasspathDirectories(readable, this);
+ completedNormally = FORK // || spec.fork
+ ? runInOtherVM(status, libs, dirs)
+ : runInSameVM(status, libs, dirs);
+ } finally {
+ if (!completedNormally) {
+ MessageUtil.info(status, spec.toLongString());
+ MessageUtil.info(status, "sandbox: " + sandbox);
+ }
+ }
+ return completedNormally;
+ }
+ protected boolean runInSameVM(IRunStatus status, File[] libs, File[] dirs) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+ ClassLoader loader = null;
+ URL[] urls = FileUtil.getFileURLs(libs);
+ boolean completedNormally = false;
+ Class targetClass = null;
+ try {
loader = new TestClassLoader(urls, dirs);
// make the following load test optional
// Class testAspect = loader.loadClass("org.aspectj.lang.JoinPoint");
MessageUtil.fail(status, null, e);
} finally {
if (!completedNormally) {
- MessageUtil.info(status, spec.toLongString());
MessageUtil.info(status, "targetClass: " + targetClass);
- MessageUtil.info(status, "sandbox: " + sandbox);
MessageUtil.info(status, "loader: " + loader);
}
}
return completedNormally;
}
+
+ /**
+ * Run in another VM by grabbing Java, bootclasspath, classpath, etc.
+ * This assumes any exception or output to System.err is a failure,
+ * and any normal completion is a pass.
+ * @param status
+ * @param libs
+ * @param dirs
+ * @return
+ */
+ protected boolean runInOtherVM(IRunStatus status, File[] libs, File[] dirs) {
+ ArrayList cmd = new ArrayList();
+ String classpath;
+ {
+ StringBuffer cp = new StringBuffer();
+ if (!LangUtil.isEmpty(BOOTCLASSPATH)) {
+ cp.append(BOOTCLASSPATH);
+ cp.append(File.pathSeparator);
+ }
+ appendClasspath(cp, libs, dirs);
+ classpath = cp.toString();
+ }
+ String java = JAVA;
+ if (null == java) {
+ File jfile = LangUtil.getJavaExecutable(classpath);
+ if (null == jfile) {
+ throw new IllegalStateException("Unable to get java");
+ }
+ java = jfile.getAbsolutePath();
+ }
+ cmd.add(java);
+ cmd.add("-classpath");
+ cmd.add(classpath);
+ cmd.add(spec.className);
+ cmd.addAll(spec.options);
+ String[] command = (String[]) cmd.toArray(new String[0]);
+ final IMessageHandler handler = status;
+ // setup to run asynchronously, pipe streams through, and report errors
+ class DoneFlag {
+ boolean done;
+ boolean failed;
+ }
+ final StringBuffer commandLabel = new StringBuffer();
+ final DoneFlag doneFlag = new DoneFlag();
+ LangUtil.ProcessController controller
+ = new LangUtil.ProcessController() {
+ protected void doCompleting(Thrown ex, int result) {
+ if (!ex.thrown && (0 == result)) {
+ doneFlag.done = true;
+ return; // no errors
+ }
+ // handle errors
+ String context = spec.className
+ + " command \""
+ + commandLabel
+ + "\"";
+ if (null != ex.fromProcess) {
+ String m = "Exception running " + context;
+ MessageUtil.abort(handler, m, ex.fromProcess);
+ doneFlag.failed = true;
+ } else if (0 != result) {
+ String m = result + " result code from running " + context;
+ MessageUtil.fail(handler, m);
+ doneFlag.failed = true;
+ }
+ if (null != ex.fromInPipe) {
+ String m = "Error processing input pipe for " + context;
+ MessageUtil.abort(handler, m, ex.fromInPipe);
+ doneFlag.failed = true;
+ }
+ if (null != ex.fromOutPipe) {
+ String m = "Error processing output pipe for " + context;
+ MessageUtil.abort(handler, m, ex.fromOutPipe);
+ doneFlag.failed = true;
+ }
+ if (null != ex.fromErrPipe) {
+ String m = "Error processing error pipe for " + context;
+ MessageUtil.abort(handler, m, ex.fromErrPipe);
+ doneFlag.failed = true;
+ }
+ doneFlag.done = true;
+ }
+ };
+ controller.init(command, spec.className);
+ if (null != JAVA_HOME) {
+ controller.setEnvp(new String[] {"JAVA_HOME=" + JAVA_HOME});
+ }
+ commandLabel.append(Arrays.asList(controller.getCommand()).toString());
+ final ByteArrayOutputStream errSnoop
+ = new ByteArrayOutputStream();
+ controller.setErrSnoop(errSnoop);
+ controller.start();
+ // give it 3 minutes...
+ long maxTime = System.currentTimeMillis() + 3 * 60 * 1000;
+ boolean waitingForStop = false;
+ while (!doneFlag.done) {
+ if (maxTime < System.currentTimeMillis()) {
+ if (waitingForStop) { // hit second timeout - bail
+ break;
+ }
+ MessageUtil.fail(status, "timeout waiting for process");
+ doneFlag.failed = true;
+ controller.stop();
+ // wait 1 minute to evaluate results of stopping
+ waitingForStop = true;
+ maxTime = System.currentTimeMillis() + 1 * 60 * 1000;
+ }
+ try {
+ Thread.sleep(300);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ if (0 < errSnoop.size()) {
+ MessageUtil.error(handler, errSnoop.toString());
+ if (!doneFlag.failed) {
+ doneFlag.failed = true;
+ }
+ }
+ if (doneFlag.failed) {
+ MessageUtil.info(handler, "other-vm command-line: " + commandLabel);
+ }
+ return !doneFlag.failed;
+ }
+
/**
* Clear (static) testing state and setup base directory,
* unless spec.skipTesting.
public static final String XMLNAME = "run";
/**
* skip description, skip sourceLocation,
- * do keywords, do options, skip paths, do comment,
+ * do keywords, do options, skip paths, do comment,
+ * skip staging,
* do dirChanges, do messages but skip children.
*/
private static final XMLNames NAMES = new XMLNames(XMLNames.DEFAULT,
- "", "", null, null, "", null, false, false, true);
+ "", "", null, null, "", null, "", false, false, true);
/** fully-qualified name of the class to run */
protected String className;
}
}
}
-