1 /* 2 * Copyright (c) 1998, 2006, 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 /** 25 * 26 */ 27 28 import java.io.*; 29 import java.util.Arrays; 30 import java.util.Properties; 31 import java.util.StringTokenizer; 32 33 /** 34 * RMI regression test utility class that uses Runtime.exec to spawn a 35 * java process that will run a named java class. 36 */ 37 public class JavaVM { 38 39 protected Process vm = null; 40 41 private String classname = ""; 42 private String args = ""; 43 private String options = ""; 44 private OutputStream outputStream = System.out; 45 private OutputStream errorStream = System.err; 46 private String policyFileName = null; 47 48 // This is used to shorten waiting time at startup. 49 private volatile boolean started = false; 50 private boolean forcesOutput = true; // default behavior 51 52 private static void mesg(Object mesg) { 53 System.err.println("JAVAVM: " + mesg.toString()); 54 } 55 56 /** string name of the program execd by JavaVM */ 57 private static String javaProgram = "java"; 58 59 static { 60 try { 61 javaProgram = TestLibrary.getProperty("java.home", "") + 62 File.separator + "bin" + File.separator + javaProgram; 63 } catch (SecurityException se) { 64 } 65 } 66 67 public JavaVM(String classname) { 68 this.classname = classname; 69 } 70 public JavaVM(String classname, 71 String options, String args) { 72 this.classname = classname; 73 this.options = options; 74 this.args = args; 75 } 76 77 public JavaVM(String classname, 78 String options, String args, 79 OutputStream out, OutputStream err) { 80 this(classname, options, args); 81 this.outputStream = out; 82 this.errorStream = err; 83 } 84 85 /* This constructor will instantiate a JavaVM object for which caller 86 * can ask for forcing initial version output on child vm process 87 * (if forcesVersionOutput is true), or letting the started vm behave freely 88 * (when forcesVersionOutput is false). 89 */ 90 public JavaVM(String classname, 91 String options, String args, 92 OutputStream out, OutputStream err, 93 boolean forcesVersionOutput) { 94 this(classname, options, args, out, err); 95 this.forcesOutput = forcesVersionOutput; 96 } 97 98 99 public void setStarted() { 100 started = true; 101 } 102 103 // Prepends passed opts array to current options 104 public void addOptions(String[] opts) { 105 String newOpts = ""; 106 for (int i = 0 ; i < opts.length ; i ++) { 107 newOpts += " " + opts[i]; 108 } 109 newOpts += " "; 110 options = newOpts + options; 111 } 112 113 // Prepends passed arguments array to current args 114 public void addArguments(String[] arguments) { 115 String newArgs = ""; 116 for (int i = 0 ; i < arguments.length ; i ++) { 117 newArgs += " " + arguments[i]; 118 } 119 newArgs += " "; 120 args = newArgs + args; 121 } 122 123 public void setPolicyFile(String policyFileName) { 124 this.policyFileName = policyFileName; 125 } 126 127 /** 128 * This method is used for setting VM options on spawned VMs. 129 * It returns the extra command line options required 130 * to turn on jcov code coverage analysis. 131 */ 132 protected static String getCodeCoverageOptions() { 133 return TestLibrary.getExtraProperty("jcov.options",""); 134 } 135 136 public void start(Runnable runnable) throws IOException { 137 if (runnable == null) { 138 throw new NullPointerException("Runnable cannot be null."); 139 } 140 141 start(); 142 new JavaVMCallbackHandler(runnable).start(); 143 } 144 145 /** 146 * Exec the VM as specified in this object's constructor. 147 */ 148 public void start() throws IOException { 149 150 if (vm != null) return; 151 152 /* 153 * If specified, add option for policy file 154 */ 155 if (policyFileName != null) { 156 String option = "-Djava.security.policy=" + policyFileName; 157 addOptions(new String[] { option }); 158 } 159 160 addOptions(new String[] { getCodeCoverageOptions() }); 161 162 /* 163 * If forcesOutput is true : 164 * We force the new starting vm to output something so that we can know 165 * when it is effectively started by redirecting standard output through 166 * the next StreamPipe call (the vm is considered started when a first 167 * output has been streamed out). 168 * We do this by prepnding a "-showversion" option in the command line. 169 */ 170 if (forcesOutput) { 171 addOptions(new String[] {"-showversion"}); 172 } 173 174 StringTokenizer optionsTokenizer = new StringTokenizer(options); 175 StringTokenizer argsTokenizer = new StringTokenizer(args); 176 int optionsCount = optionsTokenizer.countTokens(); 177 int argsCount = argsTokenizer.countTokens(); 178 179 String javaCommand[] = new String[optionsCount + argsCount + 2]; 180 int count = 0; 181 182 javaCommand[count++] = JavaVM.javaProgram; 183 while (optionsTokenizer.hasMoreTokens()) { 184 javaCommand[count++] = optionsTokenizer.nextToken(); 185 } 186 javaCommand[count++] = classname; 187 while (argsTokenizer.hasMoreTokens()) { 188 javaCommand[count++] = argsTokenizer.nextToken(); 189 } 190 191 mesg("command = " + Arrays.asList(javaCommand).toString()); 192 System.err.println(""); 193 194 vm = Runtime.getRuntime().exec(javaCommand); 195 196 /* output from the execed process may optionally be captured. */ 197 StreamPipe.plugTogether(this, vm.getInputStream(), this.outputStream); 198 StreamPipe.plugTogether(this, vm.getErrorStream(), this.errorStream); 199 200 try { 201 if (forcesOutput) { 202 // Wait distant vm to start, by using waiting time slices of 100 ms. 203 // Wait at most for 2secs, after it considers the vm to be started. 204 final long vmStartSleepTime = 100; 205 final int maxTrials = 20; 206 int numTrials = 0; 207 while (!started && numTrials < maxTrials) { 208 numTrials++; 209 Thread.sleep(vmStartSleepTime); 210 } 211 212 // Outputs running status of distant vm 213 String message = 214 "after " + (numTrials * vmStartSleepTime) + " milliseconds"; 215 if (started) { 216 mesg("distant vm process running, " + message); 217 } 218 else { 219 mesg("unknown running status of distant vm process, " + message); 220 } 221 } 222 else { 223 // Since we have no way to know if the distant vm is started, 224 // we just consider the vm to be started after a 2secs waiting time. 225 Thread.sleep(2000); 226 mesg("distant vm considered to be started after a waiting time of 2 secs"); 227 } 228 } catch (InterruptedException e) { 229 Thread.currentThread().interrupt(); 230 mesg("Thread interrupted while checking if distant vm is started. Giving up check."); 231 mesg("Distant vm state unknown"); 232 return; 233 } 234 } 235 236 public void destroy() { 237 if (vm != null) { 238 vm.destroy(); 239 } 240 vm = null; 241 } 242 243 protected Process getVM() { 244 return vm; 245 } 246 247 /** 248 * Handles calling the callback. 249 */ 250 private class JavaVMCallbackHandler extends Thread { 251 Runnable runnable; 252 253 JavaVMCallbackHandler(Runnable runnable) { 254 this.runnable = runnable; 255 } 256 257 258 /** 259 * Wait for the Process to terminate and notify the callback. 260 */ 261 @Override 262 public void run() { 263 if (vm != null) { 264 try { 265 vm.waitFor(); 266 } catch(InterruptedException ie) { 267 // Restore the interrupted status 268 Thread.currentThread().interrupt(); 269 } 270 } 271 272 if (runnable != null) { 273 runnable.run(); 274 } 275 } 276 } 277 }