]> source.dussan.org Git - aspectj.git/commitdiff
supporting a forked java run provisionally with system properties specifying fork...
authorwisberg <wisberg>
Wed, 7 May 2003 10:07:20 +0000 (10:07 +0000)
committerwisberg <wisberg>
Wed, 7 May 2003 10:07:20 +0000 (10:07 +0000)
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.

testing-drivers/src/org/aspectj/testing/drivers/package.html
testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java
testing/src/org/aspectj/testing/harness/bridge/JavaRun.java
testing/src/org/aspectj/testing/harness/bridge/Sandbox.java

index 6c8a7ebf52bedb9dbc142159940d602e72fdc783..2243b0814ca9bb84c2d093af0900dd8ed5012fcb 100644 (file)
@@ -25,7 +25,7 @@ each component in the chain may accept and interpret:
 <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.
@@ -164,6 +164,36 @@ each component in the chain may accept and interpret:
       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:
@@ -211,9 +241,38 @@ run from a peer module directory.  When running tests elsewhere,
 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>
index bb0b617c61f0c2dc4c1476f86796dfc0073ada94..3d601128114db6f2a10df019d851cb1d4e5abfcb 100644 (file)
@@ -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");
index aa891e32a587946d02587de49b3f5a1b34d447f8..5c236dbac061d1e9e8ffb0d85ec856efafd3d008 100644 (file)
 
 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 {
         }  
      }
 }
-       
index 3ad6aad10d2b08561c1ca63627f8867e1bd4aec6..a9a74210679408d31de33db3ae04a4db33c44dc5 100644 (file)
@@ -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;
     }
 }