1 /*
   2  * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.io.File;
  25 import java.io.IOException;
  26 import java.io.OutputStream;
  27 import java.util.Arrays;
  28 import java.util.StringTokenizer;
  29 import java.util.concurrent.TimeoutException;
  30 
  31 /**
  32  * RMI regression test utility class that uses Runtime.exec to spawn a
  33  * java process that will run a named java class.
  34  */
  35 public class JavaVM {
  36 
  37     protected Process vm = null;
  38 
  39     private String classname = "";
  40     private String args = "";
  41     private String options = "";
  42     private OutputStream outputStream = System.out;
  43     private OutputStream errorStream = System.err;
  44     private String policyFileName = null;
  45     private StreamPipe outPipe;
  46     private StreamPipe errPipe;
  47 
  48     private static void mesg(Object mesg) {
  49         System.err.println("JAVAVM: " + mesg.toString());
  50     }
  51 
  52     /** string name of the program execd by JavaVM */
  53     private static String javaProgram = "java";
  54 
  55     static {
  56         try {
  57             javaProgram = TestLibrary.getProperty("java.home", "") +
  58                 File.separator + "bin" + File.separator + javaProgram;
  59         } catch (SecurityException se) {
  60         }
  61     }
  62 
  63     public JavaVM(String classname,
  64                   String options, String args) {
  65         this.classname = classname;
  66         this.options = options;
  67         this.args = args;
  68     }
  69 
  70     public JavaVM(String classname,
  71                   String options, String args,
  72                   OutputStream out, OutputStream err) {
  73         this(classname, options, args);
  74         this.outputStream = out;
  75         this.errorStream = err;
  76     }
  77 
  78     // Prepends passed opts array to current options
  79     public void addOptions(String[] opts) {
  80         String newOpts = "";
  81         for (int i = 0 ; i < opts.length ; i ++) {
  82             newOpts += " " + opts[i];
  83         }
  84         newOpts += " ";
  85         options = newOpts + options;
  86     }
  87 
  88     // Prepends passed arguments array to current args
  89     public void addArguments(String[] arguments) {
  90         String newArgs = "";
  91         for (int i = 0 ; i < arguments.length ; i ++) {
  92             newArgs += " " + arguments[i];
  93         }
  94         newArgs += " ";
  95         args = newArgs + args;
  96     }
  97 
  98     public void setPolicyFile(String policyFileName) {
  99         this.policyFileName = policyFileName;
 100     }
 101 
 102     /**
 103      * This method is used for setting VM options on spawned VMs.
 104      * It returns the extra command line options required
 105      * to turn on jcov code coverage analysis.
 106      */
 107     protected static String getCodeCoverageOptions() {
 108         return TestLibrary.getExtraProperty("jcov.options","");
 109     }
 110 
 111     /**
 112      * Exec the VM as specified in this object's constructor.
 113      */
 114     public void start() throws IOException {
 115 
 116         if (vm != null)
 117             throw new IllegalStateException("JavaVM already started");
 118 
 119         /*
 120          * If specified, add option for policy file
 121          */
 122         if (policyFileName != null) {
 123             String option = "-Djava.security.policy=" + policyFileName;
 124             addOptions(new String[] { option });
 125         }
 126 
 127         addOptions(new String[] { getCodeCoverageOptions() });
 128 
 129         StringTokenizer optionsTokenizer = new StringTokenizer(options);
 130         StringTokenizer argsTokenizer = new StringTokenizer(args);
 131         int optionsCount = optionsTokenizer.countTokens();
 132         int argsCount = argsTokenizer.countTokens();
 133 
 134         String javaCommand[] = new String[optionsCount + argsCount + 2];
 135         int count = 0;
 136 
 137         javaCommand[count++] = JavaVM.javaProgram;
 138         while (optionsTokenizer.hasMoreTokens()) {
 139             javaCommand[count++] = optionsTokenizer.nextToken();
 140         }
 141         javaCommand[count++] = classname;
 142         while (argsTokenizer.hasMoreTokens()) {
 143             javaCommand[count++] = argsTokenizer.nextToken();
 144         }
 145 
 146         mesg("command = " + Arrays.asList(javaCommand).toString());
 147 
 148         vm = Runtime.getRuntime().exec(javaCommand);
 149 
 150         /* output from the execed process may optionally be captured. */
 151         outPipe = StreamPipe.plugTogether(vm.getInputStream(), this.outputStream);
 152         errPipe = StreamPipe.plugTogether(vm.getErrorStream(), this.errorStream);
 153     }
 154 
 155     public void destroy() {
 156         if (vm != null) {
 157             vm.destroy();
 158         }
 159         vm = null;
 160     }
 161 
 162     /**
 163      * Waits for the subprocess to exit, joins the pipe threads to ensure that
 164      * all output is collected, and returns its exit status.
 165      */
 166     public int waitFor() throws InterruptedException {
 167         if (vm == null)
 168             throw new IllegalStateException("can't wait for JavaVM that isn't running");
 169 
 170         int status = vm.waitFor();
 171         outPipe.join();
 172         errPipe.join();
 173         return status;
 174     }
 175 
 176     /**
 177      * Causes the current thread to wait the vm process to exit, if necessary,
 178      * wait until the vm process has terminated, or the specified waiting time
 179      * elapses. Release allocated input/output after vm process has terminated.
 180      * @param timeout the maximum milliseconds to wait.
 181      * @return exit value for vm process.
 182      * @throws InterruptedException if the current thread is interrupted
 183      *         while waiting.
 184      * @throws TimeoutException if subprocess does not end after timeout
 185      *         milliseconds passed
 186      */
 187     public int waitFor(long timeout)
 188             throws InterruptedException, TimeoutException {
 189         if (vm == null)
 190             throw new IllegalStateException("can't wait for JavaVM that isn't running");
 191         long startTime = System.currentTimeMillis();
 192         long rem = timeout;
 193 
 194         do {
 195             try {
 196                 int status = vm.exitValue();
 197                 outPipe.join();
 198                 errPipe.join();
 199                 return status;
 200             } catch (IllegalThreadStateException ex) {
 201                 if (rem > 0) {
 202                     Thread.sleep(Math.min(rem, 100));
 203                 }
 204             }
 205             rem = timeout - (System.currentTimeMillis() - startTime);
 206         } while (rem > 0);
 207         throw new TimeoutException();
 208     }
 209 
 210     /**
 211      * Starts the subprocess, waits for it to exit, and returns its exit status.
 212      */
 213     public int execute() throws IOException, InterruptedException {
 214         start();
 215         return waitFor();
 216     }
 217 }