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