From cf457e5880c719638b556cb5c1975b8edaea2d87 Mon Sep 17 00:00:00 2001 From: wisberg Date: Wed, 7 May 2003 10:07:20 +0000 Subject: [PATCH] supporting a forked java run provisionally with system properties specifying fork, java home, java, and bootclasspath. This should enable running the harness when targeting a VM that the harness or compiler can't run on, but this does not detect failures correctly in some cases, nor does it validate any of the system properties. --- .../org/aspectj/testing/drivers/package.html | 67 +++++- .../testing/harness/bridge/CompilerRun.java | 13 +- .../testing/harness/bridge/JavaRun.java | 217 ++++++++++++++++-- .../testing/harness/bridge/Sandbox.java | 34 ++- 4 files changed, 307 insertions(+), 24 deletions(-) diff --git a/testing-drivers/src/org/aspectj/testing/drivers/package.html b/testing-drivers/src/org/aspectj/testing/drivers/package.html index 6c8a7ebf5..2243b0814 100644 --- a/testing-drivers/src/org/aspectj/testing/drivers/package.html +++ b/testing-drivers/src/org/aspectj/testing/drivers/package.html @@ -25,7 +25,7 @@ each component in the chain may accept and interpret: - + + + +
ComponentOptions
Harness +
Harness

suite files, harness verbosity, temp files, option variants

suite files: ajcTest-compliant .txt or .xml files are accepted. @@ -164,6 +164,36 @@ each component in the chain may accept and interpret: or -loud is used.)
JavaRun +

Options and forking

options: 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. +
forking: + 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): +
    +
  • javarun.fork: anything to run in a new vm. +
  • +
  • javarun.java: path to the java executable to run + (suffix included). If not supplied, the harness tries to + find the java that invoked the harness. +
  • +
  • javarun.java.home: the value of the JAVA_HOME + environment variable, if it needs to be set. +
  • +
  • javarun.classpath: this is prepended to the + run classpath. Multiple entries must be separated by + the system-dependent path separator. +
  • +
+

Following are some sample configurations: @@ -211,9 +241,38 @@ run from a peer module directory. When running tests elsewhere, define the environment variable harness.libdir - e.g.,

     $ cd aspectj/tests
-    $ java -Dharness.libdir=../modules/lib/test -jar eajctesting.jar ajcTests.xml
+    $ java -Dharness.libdir=../modules/lib/test ...
+
+ +

Forking:: +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: +

+   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
+
+ +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): +
+   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..}
 
-

- \ No newline at end of file + diff --git a/testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java b/testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java index bb0b617c6..3d6011281 100644 --- a/testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java +++ b/testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java @@ -267,7 +267,11 @@ public class CompilerRun implements IAjcRun { if (0 < aspectFiles.length) { sandbox.setAspectpath(aspectFiles, checkReadable, this); } - + + // set bootclasspath, if set as system property - urk! + if (!LangUtil.isEmpty(JavaRun.BOOTCLASSPATH)) { + sandbox.setBootclasspath(JavaRun.BOOTCLASSPATH, this); + } return true; } @@ -322,7 +326,12 @@ public class CompilerRun implements IAjcRun { argList.add("-classpath"); argList.add(path); } - + path = sandbox.getBootclasspath(this); + if (!LangUtil.isEmpty(path)) { + argList.add("-bootclasspath"); + argList.add(path); + } + path = sandbox.aspectpathToString(this); if (!LangUtil.isEmpty(path)) { argList.add("-aspectpath"); diff --git a/testing/src/org/aspectj/testing/harness/bridge/JavaRun.java b/testing/src/org/aspectj/testing/harness/bridge/JavaRun.java index aa891e32a..5c236dbac 100644 --- a/testing/src/org/aspectj/testing/harness/bridge/JavaRun.java +++ b/testing/src/org/aspectj/testing/harness/bridge/JavaRun.java @@ -12,13 +12,6 @@ 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; @@ -31,10 +24,66 @@ import org.aspectj.testing.xml.XMLWriter; 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; @@ -70,18 +119,31 @@ public class JavaRun implements IAjcRun { 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"); @@ -96,15 +158,138 @@ public class JavaRun implements IAjcRun { 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. @@ -182,11 +367,12 @@ public class JavaRun implements IAjcRun { 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; @@ -297,4 +483,3 @@ public class JavaRun implements IAjcRun { } } } - diff --git a/testing/src/org/aspectj/testing/harness/bridge/Sandbox.java b/testing/src/org/aspectj/testing/harness/bridge/Sandbox.java index 3ad6aad10..a9a742106 100644 --- a/testing/src/org/aspectj/testing/harness/bridge/Sandbox.java +++ b/testing/src/org/aspectj/testing/harness/bridge/Sandbox.java @@ -108,6 +108,8 @@ public class Sandbox { /** directories and libraries on the classpath, set by CompileRun.setup(..) */ private File[] compileClasspath; + private String bootClasspath; + /** aspectpath entries, set by CompileRun.setup(..) */ private File[] aspectpath; @@ -348,6 +350,16 @@ public class Sandbox { } } + /** + * Set bootclasspath, presumed to be delimited by + * File.pathSeparator and have valid entries. + * @param bootClasspath + * @param caller + */ + void setBootclasspath(String bootClasspath, CompilerRun caller) { + this.bootClasspath = bootClasspath; + } + /** * Set compile classpath. * @param readable if true, then throw IllegalArgumentException if not readable @@ -434,7 +446,7 @@ public class Sandbox { */ String aspectpathToString(CompilerRun caller) { LangUtil.throwIaxIfNull(caller, "caller"); - return FileUtil.flatten(aspectpath, null); + return FileUtil.flatten(aspectpath, File.pathSeparator); } /** @@ -443,6 +455,24 @@ public class Sandbox { */ String classpathToString(CompilerRun caller) { LangUtil.throwIaxIfNull(caller, "caller"); - return FileUtil.flatten(compileClasspath, null); + return FileUtil.flatten(compileClasspath, File.pathSeparator); + } + + /** + * Get the bootClasspath as a String. + * @return String of bootclasspath entries delimited internally by File.pathSeparator + */ + String getBootclasspath(CompilerRun caller) { + LangUtil.throwIaxIfNull(caller, "caller"); + return bootClasspath; + } + + /** + * Get the bootClasspath as a String. + * @return String of bootclasspath entries delimited internally by File.pathSeparator + */ + String getBootclasspath(JavaRun caller) { + LangUtil.throwIaxIfNull(caller, "caller"); + return bootClasspath; } } -- 2.39.5