diff options
author | wisberg <wisberg> | 2003-05-07 10:07:20 +0000 |
---|---|---|
committer | wisberg <wisberg> | 2003-05-07 10:07:20 +0000 |
commit | cf457e5880c719638b556cb5c1975b8edaea2d87 (patch) | |
tree | 2c6844e37d45d71c2ecc00d5650d1533de685a95 /testing | |
parent | 6519e48d6e5aa4799c26a075638af3a5e1d779cd (diff) | |
download | aspectj-cf457e5880c719638b556cb5c1975b8edaea2d87.tar.gz aspectj-cf457e5880c719638b556cb5c1975b8edaea2d87.zip |
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.
Diffstat (limited to 'testing')
3 files changed, 244 insertions, 20 deletions
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; } } |