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