1 /* 2 * Copyright (c) 1998, 2017, 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.BufferedReader; 25 import java.io.ByteArrayOutputStream; 26 import java.io.DataInputStream; 27 import java.io.File; 28 import java.io.IOException; 29 import java.io.InputStreamReader; 30 import java.io.OutputStream; 31 import java.util.Arrays; 32 import java.util.StringTokenizer; 33 import java.util.concurrent.TimeoutException; 34 35 /** 36 * RMI regression test utility class that uses Runtime.exec to spawn a 37 * java process that will run a named java class. 38 */ 39 public class JavaVM { 40 41 static class CachedOutputStream extends OutputStream { 42 ByteArrayOutputStream ba; 43 OutputStream os; 44 CachedOutputStream(OutputStream os)45 public CachedOutputStream(OutputStream os) { 46 this.os = os; 47 this.ba = new ByteArrayOutputStream(); 48 } 49 write(int b)50 public void write(int b) throws IOException { 51 ba.write(b); 52 os.write(b); 53 } 54 reset()55 public void reset() throws IOException { 56 os.flush(); 57 ba.reset(); 58 } 59 } 60 61 public static final long POLLTIME_MS = 100L; 62 63 protected Process vm = null; 64 65 private String classname = ""; 66 protected String args = ""; 67 protected String options = ""; 68 protected CachedOutputStream outputStream = new CachedOutputStream(System.out); 69 protected CachedOutputStream errorStream = new CachedOutputStream(System.err); 70 private String policyFileName = null; 71 private StreamPipe outPipe; 72 private StreamPipe errPipe; 73 mesg(Object mesg)74 private static void mesg(Object mesg) { 75 System.err.println("JAVAVM: " + mesg.toString()); 76 } 77 78 /** string name of the program execd by JavaVM */ 79 private static String javaProgram = "java"; 80 81 static { 82 try { 83 javaProgram = TestLibrary.getProperty("java.home", "") + 84 File.separator + "bin" + File.separator + javaProgram; 85 } catch (SecurityException se) { 86 } 87 } 88 JavaVM(String classname, String options, String args)89 public JavaVM(String classname, 90 String options, String args) { 91 this.classname = classname; 92 this.options = options; 93 this.args = args; 94 } 95 JavaVM(String classname, String options, String args, OutputStream out, OutputStream err)96 public JavaVM(String classname, 97 String options, String args, 98 OutputStream out, OutputStream err) { 99 this(classname, options, args); 100 this.outputStream = new CachedOutputStream(out); 101 this.errorStream = new CachedOutputStream(err); 102 } 103 104 // Prepends passed opts array to current options addOptions(String... opts)105 public void addOptions(String... opts) { 106 String newOpts = ""; 107 for (int i = 0 ; i < opts.length ; i ++) { 108 newOpts += " " + opts[i]; 109 } 110 newOpts += " "; 111 options = newOpts + options; 112 } 113 114 // Prepends passed arguments array to current args addArguments(String... arguments)115 public void addArguments(String... arguments) { 116 String newArgs = ""; 117 for (int i = 0 ; i < arguments.length ; i ++) { 118 newArgs += " " + arguments[i]; 119 } 120 newArgs += " "; 121 args = newArgs + args; 122 } 123 setPolicyFile(String policyFileName)124 public void setPolicyFile(String policyFileName) { 125 this.policyFileName = policyFileName; 126 } 127 128 /** 129 * This method is used for setting VM options on spawned VMs. 130 * It returns the extra command line options required 131 * to turn on jcov code coverage analysis. 132 */ getCodeCoverageOptions()133 protected static String getCodeCoverageOptions() { 134 return TestLibrary.getExtraProperty("jcov.options",""); 135 } 136 137 /** 138 * Exec the VM as specified in this object's constructor. 139 */ start0()140 private void start0() throws IOException { 141 outputStream.reset(); 142 errorStream.reset(); 143 144 if (vm != null) 145 throw new IllegalStateException("JavaVM already started"); 146 147 /* 148 * If specified, add option for policy file 149 */ 150 if (policyFileName != null) { 151 String option = "-Djava.security.policy=" + policyFileName; 152 addOptions(new String[] { option }); 153 } 154 155 addOptions(new String[] { 156 getCodeCoverageOptions(), 157 TestParams.testJavaOpts, 158 TestParams.testVmOpts 159 }); 160 161 StringTokenizer optionsTokenizer = new StringTokenizer(options); 162 StringTokenizer argsTokenizer = new StringTokenizer(args); 163 int optionsCount = optionsTokenizer.countTokens(); 164 int argsCount = argsTokenizer.countTokens(); 165 166 String javaCommand[] = new String[optionsCount + argsCount + 2]; 167 int count = 0; 168 169 javaCommand[count++] = JavaVM.javaProgram; 170 while (optionsTokenizer.hasMoreTokens()) { 171 javaCommand[count++] = optionsTokenizer.nextToken(); 172 } 173 javaCommand[count++] = classname; 174 while (argsTokenizer.hasMoreTokens()) { 175 javaCommand[count++] = argsTokenizer.nextToken(); 176 } 177 178 mesg("command = " + Arrays.asList(javaCommand).toString()); 179 180 vm = Runtime.getRuntime().exec(javaCommand); 181 } 182 start()183 public void start() throws IOException { 184 start0(); 185 186 /* output from the exec'ed process may optionally be captured. */ 187 outPipe = StreamPipe.plugTogether(vm.getInputStream(), this.outputStream); 188 errPipe = StreamPipe.plugTogether(vm.getErrorStream(), this.errorStream); 189 } 190 startAndGetPort()191 public int startAndGetPort() throws IOException { 192 start0(); 193 194 int port = -1; 195 if (options.contains("java.nio.channels.spi.SelectorProvider=RMIDSelectorProvider")) { 196 // Obtain the server socket channel's ephemeral port number of the 197 // child rmid process. 198 BufferedReader reader = new BufferedReader( 199 new InputStreamReader(vm.getInputStream())); 200 String s; 201 while ((s = reader.readLine()) != null) { 202 System.out.println(s); 203 int i = s.indexOf(RMID.EPHEMERAL_MSG); 204 if (i != -1) { 205 String v = s.substring(RMID.EPHEMERAL_MSG.length()); 206 port = Integer.valueOf(v); 207 break; 208 } 209 } 210 if (port == -1) { 211 // something failed 212 reader = new BufferedReader(new InputStreamReader(vm.getErrorStream())); 213 while ((s = reader.readLine()) != null) 214 System.err.println(s); 215 } 216 } 217 218 /* output from the exec'ed process may optionally be captured. */ 219 outPipe = StreamPipe.plugTogether(vm.getInputStream(), this.outputStream); 220 errPipe = StreamPipe.plugTogether(vm.getErrorStream(), this.errorStream); 221 222 return port; 223 } 224 destroy()225 public void destroy() { 226 if (vm != null) { 227 vm.destroyForcibly(); 228 } 229 vm = null; 230 } 231 232 /** 233 * Return exit value for vm process. 234 * @return exit value for vm process 235 * @throws IllegalThreadStateException if the vm process has not yet terminated 236 */ exitValue()237 public int exitValue() { 238 return vm.exitValue(); 239 } 240 241 /** 242 * Destroy the vm process, and do necessary cleanup. 243 */ cleanup()244 public void cleanup() { 245 destroy(); 246 } 247 248 /** 249 * Destroys the VM, waits for it to terminate, and returns 250 * its exit status. 251 * 252 * @throws IllegalStateException if the VM has already been destroyed 253 * @throws InterruptedException if the caller is interrupted while waiting 254 */ terminate()255 public int terminate() throws InterruptedException { 256 if (vm == null) { 257 throw new IllegalStateException("JavaVM already destroyed"); 258 } 259 260 vm.destroy(); 261 int status = waitFor(); 262 vm = null; 263 return status; 264 } 265 266 267 /** 268 * Waits for the subprocess to exit, joins the pipe threads to ensure that 269 * all output is collected, and returns its exit status. 270 */ waitFor()271 public int waitFor() throws InterruptedException { 272 if (vm == null) 273 throw new IllegalStateException("can't wait for JavaVM that isn't running"); 274 275 int status = vm.waitFor(); 276 outPipe.join(); 277 errPipe.join(); 278 return status; 279 } 280 281 /** 282 * Causes the current thread to wait the vm process to exit, if necessary, 283 * wait until the vm process has terminated, or the specified waiting time 284 * elapses. Release allocated input/output after vm process has terminated. 285 * @param timeout the maximum milliseconds to wait. 286 * @return exit value for vm process. 287 * @throws InterruptedException if the current thread is interrupted 288 * while waiting. 289 * @throws TimeoutException if subprocess does not end after timeout 290 * milliseconds passed 291 */ waitFor(long timeout)292 public int waitFor(long timeout) 293 throws InterruptedException, TimeoutException { 294 if (vm == null) 295 throw new IllegalStateException("can't wait for JavaVM that isn't running"); 296 long deadline = TestLibrary.computeDeadline(System.currentTimeMillis(), timeout); 297 298 while (true) { 299 try { 300 int status = vm.exitValue(); 301 outPipe.join(); 302 errPipe.join(); 303 return status; 304 } catch (IllegalThreadStateException ignore) { } 305 306 if (System.currentTimeMillis() > deadline) 307 throw new TimeoutException(); 308 309 Thread.sleep(POLLTIME_MS); 310 } 311 } 312 313 /** 314 * Starts the subprocess, waits for it to exit, and returns its exit status. 315 */ execute()316 public int execute() throws IOException, InterruptedException { 317 start(); 318 return waitFor(); 319 } 320 } 321