1 /* 2 * Copyright (c) 2013, 2015, 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 package jdk.testlibrary; 25 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.OutputStream; 29 import java.io.PrintStream; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.Collections; 33 import java.util.concurrent.CountDownLatch; 34 import java.util.Map; 35 import java.util.concurrent.ExecutionException; 36 import java.util.concurrent.Future; 37 import java.util.concurrent.TimeUnit; 38 import java.util.concurrent.TimeoutException; 39 import java.util.function.Predicate; 40 import java.util.function.Consumer; 41 import java.util.stream.Collectors; 42 43 44 /** 45 * @deprecated This class is deprecated. Use the one from 46 * {@code <root>/test/lib/share/classes/jdk/test/lib/process} 47 */ 48 @Deprecated 49 public final class ProcessTools { 50 private static final class LineForwarder extends StreamPumper.LinePump { 51 private final PrintStream ps; 52 private final String prefix; 53 LineForwarder(String prefix, PrintStream os) { 54 this.ps = os; 55 this.prefix = prefix; 56 } 57 @Override 58 protected void processLine(String line) { 59 ps.println("[" + prefix + "] " + line); 60 } 61 } 62 63 private ProcessTools() { 64 } 65 66 /** 67 * <p>Starts a process from its builder.</p> 68 * <span>The default redirects of STDOUT and STDERR are started</span> 69 * @param name The process name 70 * @param processBuilder The process builder 71 * @return Returns the initialized process 72 * @throws IOException 73 */ 74 public static Process startProcess(String name, 75 ProcessBuilder processBuilder) 76 throws IOException { 77 return startProcess(name, processBuilder, (Consumer<String>)null); 78 } 79 80 /** 81 * <p>Starts a process from its builder.</p> 82 * <span>The default redirects of STDOUT and STDERR are started</span> 83 * <p>It is possible to monitor the in-streams via the provided {@code consumer} 84 * @param name The process name 85 * @param consumer {@linkplain Consumer} instance to process the in-streams 86 * @param processBuilder The process builder 87 * @return Returns the initialized process 88 * @throws IOException 89 */ 90 @SuppressWarnings("overloads") 91 public static Process startProcess(String name, 92 ProcessBuilder processBuilder, 93 Consumer<String> consumer) 94 throws IOException { 95 try { 96 return startProcess(name, processBuilder, consumer, null, -1, TimeUnit.NANOSECONDS); 97 } catch (InterruptedException | TimeoutException e) { 98 // will never happen 99 throw new RuntimeException(e); 100 } 101 } 102 103 /** 104 * <p>Starts a process from its builder.</p> 105 * <span>The default redirects of STDOUT and STDERR are started</span> 106 * <p> 107 * It is possible to wait for the process to get to a warmed-up state 108 * via {@linkplain Predicate} condition on the STDOUT 109 * </p> 110 * @param name The process name 111 * @param processBuilder The process builder 112 * @param linePredicate The {@linkplain Predicate} to use on the STDOUT 113 * Used to determine the moment the target app is 114 * properly warmed-up. 115 * It can be null - in that case the warmup is skipped. 116 * @param timeout The timeout for the warmup waiting; -1 = no wait; 0 = wait forever 117 * @param unit The timeout {@linkplain TimeUnit} 118 * @return Returns the initialized {@linkplain Process} 119 * @throws IOException 120 * @throws InterruptedException 121 * @throws TimeoutException 122 */ 123 public static Process startProcess(String name, 124 ProcessBuilder processBuilder, 125 final Predicate<String> linePredicate, 126 long timeout, 127 TimeUnit unit) 128 throws IOException, InterruptedException, TimeoutException { 129 return startProcess(name, processBuilder, null, linePredicate, timeout, unit); 130 } 131 132 /** 133 * <p>Starts a process from its builder.</p> 134 * <span>The default redirects of STDOUT and STDERR are started</span> 135 * <p> 136 * It is possible to wait for the process to get to a warmed-up state 137 * via {@linkplain Predicate} condition on the STDOUT and monitor the 138 * in-streams via the provided {@linkplain Consumer} 139 * </p> 140 * @param name The process name 141 * @param processBuilder The process builder 142 * @param lineConsumer The {@linkplain Consumer} the lines will be forwarded to 143 * @param linePredicate The {@linkplain Predicate} to use on the STDOUT 144 * Used to determine the moment the target app is 145 * properly warmed-up. 146 * It can be null - in that case the warmup is skipped. 147 * @param timeout The timeout for the warmup waiting; -1 = no wait; 0 = wait forever 148 * @param unit The timeout {@linkplain TimeUnit} 149 * @return Returns the initialized {@linkplain Process} 150 * @throws IOException 151 * @throws InterruptedException 152 * @throws TimeoutException 153 */ 154 public static Process startProcess(String name, 155 ProcessBuilder processBuilder, 156 final Consumer<String> lineConsumer, 157 final Predicate<String> linePredicate, 158 long timeout, 159 TimeUnit unit) 160 throws IOException, InterruptedException, TimeoutException { 161 System.out.println("["+name+"]:" + processBuilder.command().stream().collect(Collectors.joining(" "))); 162 Process p = processBuilder.start(); 163 StreamPumper stdout = new StreamPumper(p.getInputStream()); 164 StreamPumper stderr = new StreamPumper(p.getErrorStream()); 165 166 stdout.addPump(new LineForwarder(name, System.out)); 167 stderr.addPump(new LineForwarder(name, System.err)); 168 if (lineConsumer != null) { 169 StreamPumper.LinePump pump = new StreamPumper.LinePump() { 170 @Override 171 protected void processLine(String line) { 172 lineConsumer.accept(line); 173 } 174 }; 175 stdout.addPump(pump); 176 stderr.addPump(pump); 177 } 178 179 180 CountDownLatch latch = new CountDownLatch(1); 181 if (linePredicate != null) { 182 StreamPumper.LinePump pump = new StreamPumper.LinePump() { 183 @Override 184 protected void processLine(String line) { 185 if (latch.getCount() > 0 && linePredicate.test(line)) { 186 latch.countDown(); 187 } 188 } 189 }; 190 stdout.addPump(pump); 191 stderr.addPump(pump); 192 } else { 193 latch.countDown(); 194 } 195 final Future<Void> stdoutTask = stdout.process(); 196 final Future<Void> stderrTask = stderr.process(); 197 198 try { 199 if (timeout > -1) { 200 if (timeout == 0) { 201 latch.await(); 202 } else { 203 if (!latch.await(Utils.adjustTimeout(timeout), unit)) { 204 throw new TimeoutException(); 205 } 206 } 207 } 208 } catch (TimeoutException | InterruptedException e) { 209 System.err.println("Failed to start a process (thread dump follows)"); 210 for(Map.Entry<Thread, StackTraceElement[]> s : Thread.getAllStackTraces().entrySet()) { 211 printStack(s.getKey(), s.getValue()); 212 } 213 214 if (p.isAlive()) { 215 p.destroyForcibly(); 216 } 217 218 stdoutTask.cancel(true); 219 stderrTask.cancel(true); 220 throw e; 221 } 222 223 return new ProcessImpl(p, stdoutTask, stderrTask); 224 } 225 226 /** 227 * <p>Starts a process from its builder.</p> 228 * <span>The default redirects of STDOUT and STDERR are started</span> 229 * <p> 230 * It is possible to wait for the process to get to a warmed-up state 231 * via {@linkplain Predicate} condition on the STDOUT. The warm-up will 232 * wait indefinitely. 233 * </p> 234 * @param name The process name 235 * @param processBuilder The process builder 236 * @param linePredicate The {@linkplain Predicate} to use on the STDOUT 237 * Used to determine the moment the target app is 238 * properly warmed-up. 239 * It can be null - in that case the warmup is skipped. 240 * @return Returns the initialized {@linkplain Process} 241 * @throws IOException 242 * @throws InterruptedException 243 * @throws TimeoutException 244 */ 245 @SuppressWarnings("overloads") 246 public static Process startProcess(String name, 247 ProcessBuilder processBuilder, 248 final Predicate<String> linePredicate) 249 throws IOException, InterruptedException, TimeoutException { 250 return startProcess(name, processBuilder, linePredicate, 0, TimeUnit.SECONDS); 251 } 252 253 /** 254 * Get the process id of the current running Java process 255 * 256 * @return Process id 257 */ 258 public static long getProcessId() { 259 return ProcessHandle.current().getPid(); 260 } 261 262 /** 263 * Get platform specific VM arguments (e.g. -d64 on 64bit Solaris) 264 * 265 * @return String[] with platform specific arguments, empty if there are 266 * none 267 */ 268 public static String[] getPlatformSpecificVMArgs() { 269 String osName = System.getProperty("os.name"); 270 String dataModel = System.getProperty("sun.arch.data.model"); 271 272 if (osName.equals("SunOS") && dataModel.equals("64")) { 273 return new String[] { "-d64" }; 274 } 275 276 return new String[] {}; 277 } 278 279 /** 280 * Create ProcessBuilder using the java launcher from the jdk to be tested, 281 * and with any platform specific arguments prepended. 282 * 283 * @param command Arguments to pass to the java command. 284 * @return The ProcessBuilder instance representing the java command. 285 */ 286 public static ProcessBuilder createJavaProcessBuilder(String... command) 287 throws Exception { 288 return createJavaProcessBuilder(false, command); 289 } 290 291 /** 292 * Create ProcessBuilder using the java launcher from the jdk to be tested, 293 * and with any platform specific arguments prepended. 294 * 295 * @param addTestVmAndJavaOptions If true, adds test.vm.opts and test.java.opts 296 * to the java arguments. 297 * @param command Arguments to pass to the java command. 298 * @return The ProcessBuilder instance representing the java command. 299 */ 300 public static ProcessBuilder createJavaProcessBuilder(boolean addTestVmAndJavaOptions, String... command) throws Exception { 301 String javapath = JDKToolFinder.getJDKTool("java"); 302 303 ArrayList<String> args = new ArrayList<>(); 304 args.add(javapath); 305 Collections.addAll(args, getPlatformSpecificVMArgs()); 306 307 if (addTestVmAndJavaOptions) { 308 // -cp is needed to make sure the same classpath is used whether the test is 309 // run in AgentVM mode or OtherVM mode. It was added to the hotspot version 310 // of this API as part of 8077608. However, for the jdk version it is only 311 // added when addTestVmAndJavaOptions is true in order to minimize 312 // disruption to existing JDK tests, which have yet to be tested with -cp 313 // being added. At some point -cp should always be added to be consistent 314 // with what the hotspot version does. 315 args.add("-cp"); 316 args.add(System.getProperty("java.class.path")); 317 Collections.addAll(args, Utils.getTestJavaOpts()); 318 } 319 320 Collections.addAll(args, command); 321 322 // Reporting 323 StringBuilder cmdLine = new StringBuilder(); 324 for (String cmd : args) 325 cmdLine.append(cmd).append(' '); 326 System.out.println("Command line: [" + cmdLine.toString() + "]"); 327 328 return new ProcessBuilder(args.toArray(new String[args.size()])); 329 } 330 331 private static void printStack(Thread t, StackTraceElement[] stack) { 332 System.out.println("\t" + t + 333 " stack: (length = " + stack.length + ")"); 334 if (t != null) { 335 for (StackTraceElement stack1 : stack) { 336 System.out.println("\t" + stack1); 337 } 338 System.out.println(); 339 } 340 } 341 342 /** 343 * Executes a test java process, waits for it to finish and returns the process output. 344 * The default options from jtreg, test.vm.opts and test.java.opts, are added. 345 * The java from the test.jdk is used to execute the command. 346 * 347 * The command line will be like: 348 * {test.jdk}/bin/java {test.vm.opts} {test.java.opts} cmds 349 * 350 * The java process will have exited before this method returns. 351 * 352 * @param cmds User specifed arguments. 353 * @return The output from the process. 354 */ 355 public static OutputAnalyzer executeTestJava(String... options) throws Exception { 356 ProcessBuilder pb = createJavaProcessBuilder(Utils.addTestJavaOpts(options)); 357 return executeProcess(pb); 358 } 359 360 /** 361 * @deprecated Use executeTestJava instead 362 */ 363 public static OutputAnalyzer executeTestJvm(String... options) throws Exception { 364 return executeTestJava(options); 365 } 366 367 /** 368 * Executes a process, waits for it to finish and returns the process output. 369 * The process will have exited before this method returns. 370 * @param pb The ProcessBuilder to execute. 371 * @return The {@linkplain OutputAnalyzer} instance wrapping the process. 372 */ 373 public static OutputAnalyzer executeProcess(ProcessBuilder pb) throws Exception { 374 return executeProcess(pb, null); 375 } 376 377 /** 378 * Executes a process, pipe some text into its STDIN, waits for it 379 * to finish and returns the process output. The process will have exited 380 * before this method returns. 381 * @param pb The ProcessBuilder to execute. 382 * @param input The text to pipe into STDIN. Can be null. 383 * @return The {@linkplain OutputAnalyzer} instance wrapping the process. 384 */ 385 public static OutputAnalyzer executeProcess(ProcessBuilder pb, String input) 386 throws Exception { 387 OutputAnalyzer output = null; 388 Process p = null; 389 boolean failed = false; 390 try { 391 p = pb.start(); 392 if (input != null) { 393 try (OutputStream os = p.getOutputStream(); 394 PrintStream ps = new PrintStream(os)) { 395 ps.print(input); 396 ps.flush(); 397 } 398 } 399 output = new OutputAnalyzer(p); 400 p.waitFor(); 401 402 return output; 403 } catch (Throwable t) { 404 if (p != null) { 405 p.destroyForcibly().waitFor(); 406 } 407 408 failed = true; 409 System.out.println("executeProcess() failed: " + t); 410 throw t; 411 } finally { 412 if (failed) { 413 System.err.println(getProcessLog(pb, output)); 414 } 415 } 416 } 417 418 /** 419 * Executes a process, waits for it to finish and returns the process output. 420 * 421 * The process will have exited before this method returns. 422 * 423 * @param cmds The command line to execute. 424 * @return The output from the process. 425 */ 426 public static OutputAnalyzer executeProcess(String... cmds) throws Exception { 427 return executeProcess(new ProcessBuilder(cmds)); 428 } 429 430 /** 431 * Used to log command line, stdout, stderr and exit code from an executed process. 432 * @param pb The executed process. 433 * @param output The output from the process. 434 */ 435 public static String getProcessLog(ProcessBuilder pb, OutputAnalyzer output) { 436 String stderr = output == null ? "null" : output.getStderr(); 437 String stdout = output == null ? "null" : output.getStdout(); 438 String exitValue = output == null ? "null": Integer.toString(output.getExitValue()); 439 StringBuilder logMsg = new StringBuilder(); 440 final String nl = System.getProperty("line.separator"); 441 logMsg.append("--- ProcessLog ---" + nl); 442 logMsg.append("cmd: " + getCommandLine(pb) + nl); 443 logMsg.append("exitvalue: " + exitValue + nl); 444 logMsg.append("stderr: " + stderr + nl); 445 logMsg.append("stdout: " + stdout + nl); 446 447 return logMsg.toString(); 448 } 449 450 /** 451 * @return The full command line for the ProcessBuilder. 452 */ 453 public static String getCommandLine(ProcessBuilder pb) { 454 if (pb == null) { 455 return "null"; 456 } 457 StringBuilder cmd = new StringBuilder(); 458 for (String s : pb.command()) { 459 cmd.append(s).append(" "); 460 } 461 return cmd.toString().trim(); 462 } 463 464 /** 465 * Executes a process, waits for it to finish, prints the process output 466 * to stdout, and returns the process output. 467 * 468 * The process will have exited before this method returns. 469 * 470 * @param cmds The command line to execute. 471 * @return The {@linkplain OutputAnalyzer} instance wrapping the process. 472 */ 473 public static OutputAnalyzer executeCommand(String... cmds) 474 throws Throwable { 475 String cmdLine = Arrays.stream(cmds).collect(Collectors.joining(" ")); 476 System.out.println("Command line: [" + cmdLine + "]"); 477 OutputAnalyzer analyzer = ProcessTools.executeProcess(cmds); 478 System.out.println(analyzer.getOutput()); 479 return analyzer; 480 } 481 482 /** 483 * Executes a process, waits for it to finish, prints the process output 484 * to stdout and returns the process output. 485 * 486 * The process will have exited before this method returns. 487 * 488 * @param pb The ProcessBuilder to execute. 489 * @return The {@linkplain OutputAnalyzer} instance wrapping the process. 490 */ 491 public static OutputAnalyzer executeCommand(ProcessBuilder pb) 492 throws Throwable { 493 String cmdLine = pb.command().stream().collect(Collectors.joining(" ")); 494 System.out.println("Command line: [" + cmdLine + "]"); 495 OutputAnalyzer analyzer = ProcessTools.executeProcess(pb); 496 System.out.println(analyzer.getOutput()); 497 return analyzer; 498 } 499 500 private static class ProcessImpl extends Process { 501 502 private final Process p; 503 private final Future<Void> stdoutTask; 504 private final Future<Void> stderrTask; 505 506 public ProcessImpl(Process p, Future<Void> stdoutTask, Future<Void> stderrTask) { 507 this.p = p; 508 this.stdoutTask = stdoutTask; 509 this.stderrTask = stderrTask; 510 } 511 512 @Override 513 public OutputStream getOutputStream() { 514 return p.getOutputStream(); 515 } 516 517 @Override 518 public InputStream getInputStream() { 519 return p.getInputStream(); 520 } 521 522 @Override 523 public InputStream getErrorStream() { 524 return p.getErrorStream(); 525 } 526 527 @Override 528 public int waitFor() throws InterruptedException { 529 int rslt = p.waitFor(); 530 waitForStreams(); 531 return rslt; 532 } 533 534 @Override 535 public int exitValue() { 536 return p.exitValue(); 537 } 538 539 @Override 540 public void destroy() { 541 p.destroy(); 542 } 543 544 @Override 545 public long getPid() { 546 return p.getPid(); 547 } 548 549 @Override 550 public boolean isAlive() { 551 return p.isAlive(); 552 } 553 554 @Override 555 public Process destroyForcibly() { 556 return p.destroyForcibly(); 557 } 558 559 @Override 560 public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException { 561 boolean rslt = p.waitFor(timeout, unit); 562 if (rslt) { 563 waitForStreams(); 564 } 565 return rslt; 566 } 567 568 private void waitForStreams() throws InterruptedException { 569 try { 570 stdoutTask.get(); 571 } catch (ExecutionException e) { 572 } 573 try { 574 stderrTask.get(); 575 } catch (ExecutionException e) { 576 } 577 } 578 } 579 }