| /* | |
| * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package java.lang; | |
| import jdk.internal.misc.Blocker; | |
| import jdk.internal.util.StaticProperty; | |
| import java.io.*; | |
| import java.lang.ProcessBuilder.Redirect; | |
| import java.nio.charset.Charset; | |
| import java.nio.charset.UnsupportedCharsetException; | |
| import java.time.Duration; | |
| import java.util.Objects; | |
| import java.util.concurrent.CompletableFuture; | |
| import java.util.concurrent.ForkJoinPool; | |
| import java.util.concurrent.TimeUnit; | |
| import java.util.stream.Stream; | |
| /** | |
| * {@code Process} provides control of native processes started by | |
| * {@code ProcessBuilder.start} and {@code Runtime.exec}. | |
| * The class provides methods for performing input from the process, performing | |
| * output to the process, waiting for the process to complete, | |
| * checking the exit status of the process, and destroying (killing) | |
| * the process. | |
| * The {@link ProcessBuilder#start()} and | |
| * {@link Runtime#exec(String[],String[],File) Runtime.exec} | |
| * methods create a native process and return an instance of a | |
| * subclass of {@code Process} that can be used to control the process | |
| * and obtain information about it. | |
| * | |
| * <p>The methods that create processes may not work well for special | |
| * processes on certain native platforms, such as native windowing | |
| * processes, daemon processes, Win16/DOS processes on Microsoft | |
| * Windows, or shell scripts. | |
| * | |
| * <p>By default, the created process does not have its own terminal | |
| * or console. All its standard I/O (i.e. stdin, stdout, stderr) | |
| * operations will be redirected to the parent process, where they can | |
| * be accessed via the streams obtained using the methods | |
| * {@link #getOutputStream()}, | |
| * {@link #getInputStream()}, and | |
| * {@link #getErrorStream()}. | |
| * The I/O streams of characters and lines can be written and read using the methods | |
| * {@link #outputWriter()}, {@link #outputWriter(Charset)}, | |
| * {@link #inputReader()}, {@link #inputReader(Charset)}, | |
| * {@link #errorReader()}, and {@link #errorReader(Charset)}. | |
| * The parent process uses these streams to feed input to and get output | |
| * from the process. Because some native platforms only provide | |
| * limited buffer size for standard input and output streams, failure | |
| * to promptly write the input stream or read the output stream of | |
| * the process may cause the process to block, or even deadlock. | |
| * | |
| * <p>Where desired, <a href="ProcessBuilder.html#redirect-input"> | |
| * process I/O can also be redirected</a> | |
| * using methods of the {@link ProcessBuilder} class. | |
| * | |
| * <p>There is no requirement that the process represented by a {@code | |
| * Process} object execute asynchronously or concurrently with respect | |
| * to the Java process that owns the {@code Process} object. | |
| * | |
| * <p>As of 1.5, {@link ProcessBuilder#start()} is the preferred way | |
| * to create a {@code Process}. | |
| * | |
| * <p>Subclasses of Process should override the {@link #onExit()} and | |
| * {@link #toHandle()} methods to provide a fully functional Process including the | |
| * {@linkplain #pid() process id}, | |
| * {@linkplain #info() information about the process}, | |
| * {@linkplain #children() direct children}, and | |
| * {@linkplain #descendants() direct children plus descendants of those children} of the process. | |
| * Delegating to the underlying Process or ProcessHandle is typically | |
| * easiest and most efficient. | |
| * | |
| * <h2>Resource Usage</h2> | |
| * {@linkplain ProcessBuilder#start() Starting a process} uses resources in both the invoking process and the invoked | |
| * process and for the communication streams between them. | |
| * The resources to control the process and for communication between the processes are retained | |
| * until there are no longer any references to the Process or the input, error, and output streams | |
| * or readers, or they have been closed. | |
| * | |
| * <p>The process is not killed when there are no more references to the {@code Process} object, | |
| * but rather the process continues executing asynchronously. | |
| * The process implementation closes file descriptors and handles for streams | |
| * that are no longer referenced to prevent leaking operating system resources. | |
| * Processes that have terminated or been terminated are monitored and their resources released. | |
| * | |
| * <p>Streams should be {@code closed} when they are no longer needed, to avoid delaying | |
| * releasing the operating system resources. | |
| * {@code Try-with-resources} can be used to open and close the streams. | |
| * <p>For example, to capture the output of a program known to produce some output and then exit: | |
| * {@snippet lang = "java" : | |
| * List<String> capture(List<String> args) throws Exception { | |
| * ProcessBuilder pb = new ProcessBuilder(args); | |
| * Process process = pb.start(); | |
| * try (BufferedReader in = process.inputReader()) { | |
| * List<String> captured = in.readAllLines(); | |
| * int status = process.waitFor(); | |
| * if (status != 0) { | |
| * throw new RuntimeException("Process %d: %s failed with %d" | |
| * .formatted(process.pid(), args, status)); | |
| * } | |
| * return captured; | |
| * } | |
| * } | |
| * } | |
| * <p>Stream resources (file descriptor or handle) are always paired; one in the invoking process | |
| * and the other end of that connection in the invoked process. | |
| * Closing a stream at either end terminates communication but does not have any direct effect | |
| * on the other Process. The closing of the stream typically results in the other process exiting. | |
| * | |
| * <p> {@linkplain #destroy Destroying a process} signals the operating system to terminate the process. | |
| * It is up to the operating system to clean up and release the resources of that process. | |
| * Typically, file descriptors and handles are closed. When they are closed, any connections to | |
| * other processes are terminated and file descriptors and handles in the invoking process signal | |
| * end-of-file or closed. Usually, that is seen as an end-of-file or an exception. | |
| * | |
| * @since 1.0 | |
| */ | |
| public abstract class Process { | |
| // Readers and Writers created for this process; so repeated calls return the same object | |
| // All updates must be done while synchronized on this Process. | |
| private BufferedWriter outputWriter; | |
| private Charset outputCharset; | |
| private BufferedReader inputReader; | |
| private Charset inputCharset; | |
| private BufferedReader errorReader; | |
| private Charset errorCharset; | |
| /** | |
| * Default constructor for Process. | |
| */ | |
| public Process() {} | |
| /** | |
| * Returns the output stream connected to the normal input of the | |
| * process. Output to the stream is piped into the standard | |
| * input of the process represented by this {@code Process} object. | |
| * | |
| * <p>If the standard input of the process has been redirected using | |
| * {@link ProcessBuilder#redirectInput(Redirect) | |
| * ProcessBuilder.redirectInput} | |
| * then this method will return a | |
| * <a href="ProcessBuilder.html#redirect-input">null output stream</a>. | |
| * | |
| * <p>The output stream should be {@linkplain OutputStream#close closed} | |
| * when it is no longer needed. | |
| * | |
| * @apiNote | |
| * When writing to both {@link #getOutputStream()} and either {@link #outputWriter()} | |
| * or {@link #outputWriter(Charset)}, {@link BufferedWriter#flush BufferedWriter.flush} | |
| * should be called before writes to the {@code OutputStream}. | |
| * | |
| * @implNote | |
| * Implementation note: It is a good idea for the returned | |
| * output stream to be buffered. | |
| * | |
| * @return the output stream connected to the normal input of the | |
| * process | |
| */ | |
| public abstract OutputStream getOutputStream(); | |
| /** | |
| * Returns the input stream connected to the normal output of the | |
| * process. The stream obtains data piped from the standard | |
| * output of the process represented by this {@code Process} object. | |
| * | |
| * <p>If the standard output of the process has been redirected using | |
| * {@link ProcessBuilder#redirectOutput(Redirect) | |
| * ProcessBuilder.redirectOutput} | |
| * then this method will return a | |
| * <a href="ProcessBuilder.html#redirect-output">null input stream</a>. | |
| * | |
| * <p>Otherwise, if the standard error of the process has been | |
| * redirected using | |
| * {@link ProcessBuilder#redirectErrorStream(boolean) | |
| * ProcessBuilder.redirectErrorStream} | |
| * then the input stream returned by this method will receive the | |
| * merged standard output and the standard error of the process. | |
| * | |
| * <p>The input stream should be {@linkplain InputStream#close closed} | |
| * when it is no longer needed. | |
| * | |
| * @apiNote | |
| * Use either this method or an {@linkplain #inputReader() input reader} | |
| * but not both on the same {@code Process}. | |
| * The input reader consumes and buffers bytes from the input stream. | |
| * Bytes read from the input stream would not be seen by the reader and | |
| * buffer contents are unpredictable. | |
| * | |
| * @implNote | |
| * Implementation note: It is a good idea for the returned | |
| * input stream to be buffered. | |
| * | |
| * @return the input stream connected to the normal output of the | |
| * process | |
| */ | |
| public abstract InputStream getInputStream(); | |
| /** | |
| * Returns the input stream connected to the error output of the | |
| * process. The stream obtains data piped from the error output | |
| * of the process represented by this {@code Process} object. | |
| * | |
| * <p>If the standard error of the process has been redirected using | |
| * {@link ProcessBuilder#redirectError(Redirect) | |
| * ProcessBuilder.redirectError} or | |
| * {@link ProcessBuilder#redirectErrorStream(boolean) | |
| * ProcessBuilder.redirectErrorStream} | |
| * then this method will return a | |
| * <a href="ProcessBuilder.html#redirect-output">null input stream</a>. | |
| * | |
| * <p>The error stream should be {@linkplain InputStream#close closed} | |
| * when it is no longer needed. | |
| * | |
| * @apiNote | |
| * Use either this method or an {@linkplain #errorReader() error reader} | |
| * but not both on the same {@code Process}. | |
| * The error reader consumes and buffers bytes from the error stream. | |
| * Bytes read from the error stream would not be seen by the reader and the | |
| * buffer contents are unpredictable. | |
| * | |
| * @implNote | |
| * Implementation note: It is a good idea for the returned | |
| * input stream to be buffered. | |
| * | |
| * @return the input stream connected to the error output of | |
| * the process | |
| */ | |
| public abstract InputStream getErrorStream(); | |
| /** | |
| * Returns a {@link BufferedReader BufferedReader} connected to the standard | |
| * output of the process. The {@link Charset} for the native encoding is used | |
| * to read characters, lines, or stream lines from standard output. | |
| * | |
| * <p>This method delegates to {@link #inputReader(Charset)} using the | |
| * {@link Charset} named by the {@code native.encoding} system property. | |
| * If the {@code native.encoding} is not a valid charset name or not supported | |
| * the {@link Charset#defaultCharset()} is used. | |
| * | |
| * <p>The reader should be {@linkplain BufferedReader#close closed} | |
| * when it is no longer needed. | |
| * | |
| * @apiNote | |
| * Use either this method or the {@linkplain #getInputStream input stream} | |
| * but not both on the same {@code Process}. | |
| * The input reader consumes and buffers bytes from the input stream. | |
| * Bytes read from the input stream would not be seen by the reader and the | |
| * buffer contents are unpredictable. | |
| * | |
| * @return a {@link BufferedReader BufferedReader} using the | |
| * {@code native.encoding} if supported, otherwise, the | |
| * {@link Charset#defaultCharset()} | |
| * @since 17 | |
| */ | |
| public final BufferedReader inputReader() { | |
| return inputReader(CharsetHolder.nativeCharset()); | |
| } | |
| /** | |
| * Returns a {@link BufferedReader BufferedReader} connected to the | |
| * standard output of this process using a Charset. | |
| * The {@code BufferedReader} can be used to read characters, lines, | |
| * or stream lines of the standard output. | |
| * | |
| * <p>Characters are read by an InputStreamReader that reads and decodes bytes | |
| * from this process {@link #getInputStream()}. Bytes are decoded to characters | |
| * using the {@code charset}; malformed-input and unmappable-character | |
| * sequences are replaced with the charset's default replacement. | |
| * The {@code BufferedReader} reads and buffers characters from the InputStreamReader. | |
| * | |
| * <p>The first call to this method creates the {@link BufferedReader BufferedReader}, | |
| * if called again with the same {@code charset} the same {@code BufferedReader} is returned. | |
| * It is an error to call this method again with a different {@code charset}. | |
| * | |
| * <p>If the standard output of the process has been redirected using | |
| * {@link ProcessBuilder#redirectOutput(Redirect) ProcessBuilder.redirectOutput} | |
| * then the {@code InputStreamReader} will be reading from a | |
| * <a href="ProcessBuilder.html#redirect-output">null input stream</a>. | |
| * | |
| * <p>The reader should be {@linkplain BufferedReader#close closed} | |
| * when it is no longer needed. | |
| * | |
| * <p>Otherwise, if the standard error of the process has been redirected using | |
| * {@link ProcessBuilder#redirectErrorStream(boolean) | |
| * ProcessBuilder.redirectErrorStream} then the input reader returned by | |
| * this method will receive the merged standard output and the standard error | |
| * of the process. | |
| * | |
| * @apiNote | |
| * Use either this method or the {@linkplain #getInputStream input stream} | |
| * but not both on the same {@code Process}. | |
| * The input reader consumes and buffers bytes from the input stream. | |
| * Bytes read from the input stream would not be seen by the reader and the | |
| * buffer contents are unpredictable. | |
| * | |
| * <p>When the process has terminated, and the standard input has not been redirected, | |
| * reading of the bytes available from the underlying stream is on a best effort basis and | |
| * may be unpredictable. | |
| * | |
| * @param charset the {@code Charset} used to decode bytes to characters | |
| * @return a {@code BufferedReader} for the standard output of the process using the {@code charset} | |
| * @throws NullPointerException if the {@code charset} is {@code null} | |
| * @throws IllegalStateException if called more than once with different charset arguments | |
| * @since 17 | |
| */ | |
| public final BufferedReader inputReader(Charset charset) { | |
| Objects.requireNonNull(charset, "charset"); | |
| synchronized (this) { | |
| if (inputReader == null) { | |
| inputCharset = charset; | |
| inputReader = new BufferedReader(new InputStreamReader(getInputStream(), charset)); | |
| } else { | |
| if (!inputCharset.equals(charset)) | |
| throw new IllegalStateException("BufferedReader was created with charset: " + inputCharset); | |
| } | |
| return inputReader; | |
| } | |
| } | |
| /** | |
| * Returns a {@link BufferedReader BufferedReader} connected to the standard | |
| * error of the process. The {@link Charset} for the native encoding is used | |
| * to read characters, lines, or stream lines from standard error. | |
| * | |
| * <p>This method delegates to {@link #errorReader(Charset)} using the | |
| * {@link Charset} named by the {@code native.encoding} system property. | |
| * If the {@code native.encoding} is not a valid charset name or not supported | |
| * the {@link Charset#defaultCharset()} is used. | |
| * | |
| * <p>The error reader should be {@linkplain BufferedReader#close closed} | |
| * when it is no longer needed. | |
| * | |
| * @apiNote | |
| * Use either this method or the {@linkplain #getErrorStream error stream} | |
| * but not both on the same {@code Process}. | |
| * The error reader consumes and buffers bytes from the error stream. | |
| * Bytes read from the error stream would not be seen by the reader and the | |
| * buffer contents are unpredictable. | |
| * | |
| * @return a {@link BufferedReader BufferedReader} using the | |
| * {@code native.encoding} if supported, otherwise, the | |
| * {@link Charset#defaultCharset()} | |
| * @since 17 | |
| */ | |
| public final BufferedReader errorReader() { | |
| return errorReader(CharsetHolder.nativeCharset()); | |
| } | |
| /** | |
| * Returns a {@link BufferedReader BufferedReader} connected to the | |
| * standard error of this process using a Charset. | |
| * The {@code BufferedReader} can be used to read characters, lines, | |
| * or stream lines of the standard error. | |
| * | |
| * <p>Characters are read by an InputStreamReader that reads and decodes bytes | |
| * from this process {@link #getErrorStream()}. Bytes are decoded to characters | |
| * using the {@code charset}; malformed-input and unmappable-character | |
| * sequences are replaced with the charset's default replacement. | |
| * The {@code BufferedReader} reads and buffers characters from the InputStreamReader. | |
| * | |
| * <p>The first call to this method creates the {@link BufferedReader BufferedReader}, | |
| * if called again with the same {@code charset} the same {@code BufferedReader} is returned. | |
| * It is an error to call this method again with a different {@code charset}. | |
| * | |
| * <p>If the standard error of the process has been redirected using | |
| * {@link ProcessBuilder#redirectError(Redirect) ProcessBuilder.redirectError} or | |
| * {@link ProcessBuilder#redirectErrorStream(boolean) ProcessBuilder.redirectErrorStream} | |
| * then the {@code InputStreamReader} will be reading from a | |
| * <a href="ProcessBuilder.html#redirect-output">null input stream</a>. | |
| * | |
| * <p>The error reader should be {@linkplain BufferedReader#close closed} | |
| * when it is no longer needed. | |
| * | |
| * @apiNote | |
| * Use either this method or the {@linkplain #getErrorStream error stream} | |
| * but not both on the same {@code Process}. | |
| * The error reader consumes and buffers bytes from the error stream. | |
| * Bytes read from the error stream would not be seen by the reader and the | |
| * buffer contents are unpredictable. | |
| * | |
| * <p>When the process has terminated, and the standard error has not been redirected, | |
| * reading of the bytes available from the underlying stream is on a best effort basis and | |
| * may be unpredictable. | |
| * | |
| * @param charset the {@code Charset} used to decode bytes to characters | |
| * @return a {@code BufferedReader} for the standard error of the process using the {@code charset} | |
| * @throws NullPointerException if the {@code charset} is {@code null} | |
| * @throws IllegalStateException if called more than once with different charset arguments | |
| * @since 17 | |
| */ | |
| public final BufferedReader errorReader(Charset charset) { | |
| Objects.requireNonNull(charset, "charset"); | |
| synchronized (this) { | |
| if (errorReader == null) { | |
| errorCharset = charset; | |
| errorReader = new BufferedReader(new InputStreamReader(getErrorStream(), charset)); | |
| } else { | |
| if (!errorCharset.equals(charset)) | |
| throw new IllegalStateException("BufferedReader was created with charset: " + errorCharset); | |
| } | |
| return errorReader; | |
| } | |
| } | |
| /** | |
| * Returns a {@code BufferedWriter} connected to the normal input of the process | |
| * using the native encoding. | |
| * Writes text to a character-output stream, buffering characters to provide | |
| * for the efficient writing of single characters, arrays, and strings. | |
| * | |
| * <p>This method delegates to {@link #outputWriter(Charset)} using the | |
| * {@link Charset} named by the {@code native.encoding} system property. | |
| * If the {@code native.encoding} is not a valid charset name or not supported | |
| * the {@link Charset#defaultCharset()} is used. | |
| * | |
| * <p>The output writer should be {@linkplain BufferedWriter#close closed} | |
| * when it is no longer needed. | |
| * | |
| * @return a {@code BufferedWriter} to the standard input of the process using the charset | |
| * for the {@code native.encoding} system property | |
| * @since 17 | |
| */ | |
| public final BufferedWriter outputWriter() { | |
| return outputWriter(CharsetHolder.nativeCharset()); | |
| } | |
| /** | |
| * Returns a {@code BufferedWriter} connected to the normal input of the process | |
| * using a Charset. | |
| * Writes text to a character-output stream, buffering characters to provide | |
| * for the efficient writing of single characters, arrays, and strings. | |
| * | |
| * <p>Characters written by the writer are encoded to bytes using {@link OutputStreamWriter} | |
| * and the {@link Charset} are written to the standard input of the process represented | |
| * by this {@code Process}. | |
| * Malformed-input and unmappable-character sequences are replaced with the charset's | |
| * default replacement. | |
| * | |
| * <p>The first call to this method creates the {@link BufferedWriter BufferedWriter}, | |
| * if called again with the same {@code charset} the same {@code BufferedWriter} is returned. | |
| * It is an error to call this method again with a different {@code charset}. | |
| * | |
| * <p>If the standard input of the process has been redirected using | |
| * {@link ProcessBuilder#redirectInput(Redirect) | |
| * ProcessBuilder.redirectInput} then the {@code OutputStreamWriter} writes to a | |
| * <a href="ProcessBuilder.html#redirect-input">null output stream</a>. | |
| * | |
| * <p>The output writer should be {@linkplain BufferedWriter#close closed} | |
| * when it is no longer needed. | |
| * | |
| * @apiNote | |
| * A {@linkplain BufferedWriter} writes characters, arrays of characters, and strings. | |
| * Wrapping the {@link BufferedWriter} with a {@link PrintWriter} provides | |
| * efficient buffering and formatting of primitives and objects as well as support | |
| * for auto-flush on line endings. | |
| * Call the {@link BufferedWriter#flush()} method to flush buffered output to the process. | |
| * <p> | |
| * When writing to both {@link #getOutputStream()} and either {@link #outputWriter()} | |
| * or {@link #outputWriter(Charset)}, {@linkplain BufferedWriter#flush BufferedWriter.flush} | |
| * should be called before writes to the {@code OutputStream}. | |
| * | |
| * @param charset the {@code Charset} to encode characters to bytes | |
| * @return a {@code BufferedWriter} to the standard input of the process using the {@code charset} | |
| * @throws NullPointerException if the {@code charset} is {@code null} | |
| * @throws IllegalStateException if called more than once with different charset arguments | |
| * @since 17 | |
| */ | |
| public final BufferedWriter outputWriter(Charset charset) { | |
| Objects.requireNonNull(charset, "charset"); | |
| synchronized (this) { | |
| if (outputWriter == null) { | |
| outputCharset = charset; | |
| outputWriter = new BufferedWriter(new OutputStreamWriter(getOutputStream(), charset)); | |
| } else { | |
| if (!outputCharset.equals(charset)) | |
| throw new IllegalStateException("BufferedWriter was created with charset: " + outputCharset); | |
| } | |
| return outputWriter; | |
| } | |
| } | |
| /** | |
| * Causes the current thread to wait, if necessary, until the | |
| * process represented by this {@code Process} object has | |
| * terminated. This method returns immediately if the process | |
| * has already terminated. If the process has not yet | |
| * terminated, the calling thread will be blocked until the | |
| * process exits. | |
| * | |
| * @return the exit value of the process represented by this | |
| * {@code Process} object. By convention, the value | |
| * {@code 0} indicates normal termination. | |
| * @throws InterruptedException if the current thread is | |
| * {@linkplain Thread#interrupt() interrupted} by another | |
| * thread while it is waiting, then the wait is ended and | |
| * an {@link InterruptedException} is thrown. | |
| */ | |
| public abstract int waitFor() throws InterruptedException; | |
| /** | |
| * Causes the current thread to wait, if necessary, until the | |
| * process represented by this {@code Process} object has | |
| * terminated, or the specified waiting time elapses. | |
| * | |
| * <p>If the process has already terminated then this method returns | |
| * immediately with the value {@code true}. If the process has not | |
| * terminated and the timeout value is less than, or equal to, zero, then | |
| * this method returns immediately with the value {@code false}. | |
| * | |
| * @implSpec | |
| * The default implementation of this method polls the {@code exitValue} | |
| * to check if the process has terminated. | |
| * | |
| * @implNote | |
| * Concrete implementations of this class are strongly encouraged to | |
| * override this method with a more efficient implementation. | |
| * | |
| * @param timeout the maximum time to wait | |
| * @param unit the time unit of the {@code timeout} argument | |
| * @return {@code true} if the process has exited and {@code false} if | |
| * the waiting time elapsed before the process has exited. | |
| * @throws InterruptedException if the current thread is interrupted | |
| * while waiting. | |
| * @throws NullPointerException if unit is null | |
| * @since 1.8 | |
| */ | |
| public boolean waitFor(long timeout, TimeUnit unit) | |
| throws InterruptedException | |
| { | |
| long remainingNanos = unit.toNanos(timeout); // throw NPE before other conditions | |
| if (hasExited()) | |
| return true; | |
| if (timeout <= 0) | |
| return false; | |
| long deadline = System.nanoTime() + remainingNanos; | |
| do { | |
| Thread.sleep(Math.min(TimeUnit.NANOSECONDS.toMillis(remainingNanos) + 1, 100)); | |
| if (hasExited()) | |
| return true; | |
| remainingNanos = deadline - System.nanoTime(); | |
| } while (remainingNanos > 0); | |
| return false; | |
| } | |
| /** | |
| * Causes the current thread to wait, if necessary, until the | |
| * process represented by this {@code Process} object has | |
| * terminated, or the specified waiting duration elapses. | |
| * | |
| * <p>If the process has already terminated then this method returns | |
| * immediately with the value {@code true}. If the process has not | |
| * terminated and the duration is not positive, then | |
| * this method returns immediately with the value {@code false}. | |
| * | |
| * @implSpec | |
| * The default implementation of this method polls the {@code exitValue} | |
| * to check if the process has terminated. | |
| * | |
| * @implNote | |
| * Concrete implementations of this class are strongly encouraged to | |
| * override this method with a more efficient implementation. | |
| * | |
| * @param duration the maximum duration to wait; if not positive, | |
| * this method returns immediately. | |
| * @return {@code true} if the process has exited and {@code false} if | |
| * the waiting duration elapsed before the process has exited. | |
| * @throws InterruptedException if the current thread is interrupted | |
| * while waiting. | |
| * @throws NullPointerException if duration is null | |
| * @since 24 | |
| */ | |
| public boolean waitFor(Duration duration) throws InterruptedException { | |
| Objects.requireNonNull(duration, "duration"); | |
| return waitFor(TimeUnit.NANOSECONDS.convert(duration), TimeUnit.NANOSECONDS); | |
| } | |
| /** | |
| * Returns the exit value for the process. | |
| * | |
| * @return the exit value of the process represented by this | |
| * {@code Process} object. By convention, the value | |
| * {@code 0} indicates normal termination. | |
| * @throws IllegalThreadStateException if the process represented | |
| * by this {@code Process} object has not yet terminated | |
| */ | |
| public abstract int exitValue(); | |
| /** | |
| * Kills the process. | |
| * Whether the process represented by this {@code Process} object is | |
| * {@linkplain #supportsNormalTermination normally terminated} or not is | |
| * implementation dependent. | |
| * Forcible process destruction is defined as the immediate termination of a | |
| * process, whereas normal termination allows the process to shut down cleanly. | |
| * If the process is not alive, no action is taken. | |
| * <p> | |
| * The {@link java.util.concurrent.CompletableFuture} from {@link #onExit} is | |
| * {@linkplain java.util.concurrent.CompletableFuture#complete completed} | |
| * when the process has terminated. | |
| */ | |
| public abstract void destroy(); | |
| /** | |
| * Kills the process forcibly. The process represented by this | |
| * {@code Process} object is forcibly terminated. | |
| * Forcible process destruction is defined as the immediate termination of a | |
| * process, whereas normal termination allows the process to shut down cleanly. | |
| * If the process is not alive, no action is taken. | |
| * <p> | |
| * The {@link java.util.concurrent.CompletableFuture} from {@link #onExit} is | |
| * {@linkplain java.util.concurrent.CompletableFuture#complete completed} | |
| * when the process has terminated. | |
| * <p> | |
| * Invoking this method on {@code Process} objects returned by | |
| * {@link ProcessBuilder#start()} and {@link Runtime#exec} forcibly terminate | |
| * the process. | |
| * | |
| * @implSpec | |
| * The default implementation of this method invokes {@link #destroy} | |
| * and so may not forcibly terminate the process. | |
| * @implNote | |
| * Concrete implementations of this class are strongly encouraged to override | |
| * this method with a compliant implementation. | |
| * @apiNote | |
| * The process may not terminate immediately. | |
| * i.e. {@code isAlive()} may return true for a brief period | |
| * after {@code destroyForcibly()} is called. This method | |
| * may be chained to {@code waitFor()} if needed. | |
| * | |
| * @return the {@code Process} object representing the | |
| * process forcibly destroyed | |
| * @since 1.8 | |
| */ | |
| public Process destroyForcibly() { | |
| destroy(); | |
| return this; | |
| } | |
| /** | |
| * Returns {@code true} if the implementation of {@link #destroy} is to | |
| * normally terminate the process, | |
| * Returns {@code false} if the implementation of {@code destroy} | |
| * forcibly and immediately terminates the process. | |
| * <p> | |
| * Invoking this method on {@code Process} objects returned by | |
| * {@link ProcessBuilder#start()} and {@link Runtime#exec} return | |
| * {@code true} or {@code false} depending on the platform implementation. | |
| * | |
| * @implSpec | |
| * This implementation throws an instance of | |
| * {@link java.lang.UnsupportedOperationException} and performs no other action. | |
| * | |
| * @return {@code true} if the implementation of {@link #destroy} is to | |
| * normally terminate the process; | |
| * otherwise, {@link #destroy} forcibly terminates the process | |
| * @throws UnsupportedOperationException if the Process implementation | |
| * does not support this operation | |
| * @since 9 | |
| */ | |
| public boolean supportsNormalTermination() { | |
| throw new UnsupportedOperationException(this.getClass() | |
| + ".supportsNormalTermination() not supported" ); | |
| } | |
| /** | |
| * Tests whether the process represented by this {@code Process} is | |
| * alive. | |
| * | |
| * @return {@code true} if the process represented by this | |
| * {@code Process} object has not yet terminated. | |
| * @since 1.8 | |
| */ | |
| public boolean isAlive() { | |
| return !hasExited(); | |
| } | |
| /** | |
| * This is called from the default implementation of | |
| * {@code waitFor(long, TimeUnit)} and {@code waitFor(Duration)}, | |
| * which are specified to poll {@code exitValue()}. | |
| */ | |
| private boolean hasExited() { | |
| try { | |
| exitValue(); | |
| return true; | |
| } catch (IllegalThreadStateException e) { | |
| return false; | |
| } | |
| } | |
| /** | |
| * Returns the native process ID of the process. | |
| * The native process ID is an identification number that the operating | |
| * system assigns to the process. | |
| * | |
| * @implSpec | |
| * The implementation of this method returns the process id as: | |
| * {@link #toHandle toHandle().pid()}. | |
| * | |
| * @return the native process id of the process | |
| * @throws UnsupportedOperationException if the Process implementation | |
| * does not support this operation | |
| * @since 9 | |
| */ | |
| public long pid() { | |
| return toHandle().pid(); | |
| } | |
| /** | |
| * Returns a {@code CompletableFuture<Process>} for the termination of the Process. | |
| * The {@link java.util.concurrent.CompletableFuture} provides the ability | |
| * to trigger dependent functions or actions that may be run synchronously | |
| * or asynchronously upon process termination. | |
| * When the process has terminated the CompletableFuture is | |
| * {@link java.util.concurrent.CompletableFuture#complete completed} regardless | |
| * of the exit status of the process. | |
| * <p> | |
| * Calling {@code onExit().get()} waits for the process to terminate and returns | |
| * the Process. The future can be used to check if the process is | |
| * {@linkplain java.util.concurrent.CompletableFuture#isDone done} or to | |
| * {@linkplain java.util.concurrent.CompletableFuture#get() wait} for it to terminate. | |
| * {@linkplain java.util.concurrent.CompletableFuture#cancel(boolean) Cancelling} | |
| * the CompletableFuture does not affect the Process. | |
| * <p> | |
| * Processes returned from {@link ProcessBuilder#start()} override the | |
| * default implementation to provide an efficient mechanism to wait | |
| * for process exit. | |
| * | |
| * @apiNote | |
| * Using {@link #onExit() onExit} is an alternative to | |
| * {@link #waitFor() waitFor} that enables both additional concurrency | |
| * and convenient access to the result of the Process. | |
| * Lambda expressions can be used to evaluate the result of the Process | |
| * execution. | |
| * If there is other processing to be done before the value is used | |
| * then {@linkplain #onExit onExit} is a convenient mechanism to | |
| * free the current thread and block only if and when the value is needed. | |
| * <br> | |
| * For example, launching a process to compare two files and get a boolean if they are identical: | |
| * {@snippet lang = "java" : | |
| * Process p = new ProcessBuilder("cmp", "f1", "f2").start(); | |
| * Future<Boolean> identical = p.onExit().thenApply(p1 -> p1.exitValue() == 0); | |
| * ... | |
| * if (identical.get()) { ... } | |
| * } | |
| * | |
| * @implSpec | |
| * This implementation executes {@link #waitFor()} in a separate thread | |
| * repeatedly until it returns successfully. If the execution of | |
| * {@code waitFor} is interrupted, the thread's interrupt status is preserved. | |
| * <p> | |
| * When {@link #waitFor()} returns successfully the CompletableFuture is | |
| * {@linkplain java.util.concurrent.CompletableFuture#complete completed} regardless | |
| * of the exit status of the process. | |
| * | |
| * This implementation may consume a lot of memory for thread stacks if a | |
| * large number of processes are waited for concurrently. | |
| * <p> | |
| * External implementations should override this method and provide | |
| * a more efficient implementation. For example, to delegate to the underlying | |
| * process, it can do the following: | |
| * {@snippet lang = "java" : | |
| * public CompletableFuture<Process> onExit() { | |
| * return delegate.onExit().thenApply(p -> this); | |
| * } | |
| * } | |
| * @apiNote | |
| * The process may be observed to have terminated with {@link #isAlive} | |
| * before the ComputableFuture is completed and dependent actions are invoked. | |
| * | |
| * @return a new {@code CompletableFuture<Process>} for the Process | |
| * | |
| * @since 9 | |
| */ | |
| public CompletableFuture<Process> onExit() { | |
| return CompletableFuture.supplyAsync(this::waitForInternal); | |
| } | |
| /** | |
| * Wait for the process to exit by calling {@code waitFor}. | |
| * If the thread is interrupted, remember the interrupted state to | |
| * be restored before returning. Use ForkJoinPool.ManagedBlocker | |
| * so that the number of workers in case ForkJoinPool is used is | |
| * compensated when the thread blocks in waitFor(). | |
| * | |
| * @return the Process | |
| */ | |
| private Process waitForInternal() { | |
| boolean interrupted = false; | |
| while (true) { | |
| try { | |
| ForkJoinPool.managedBlock(new ForkJoinPool.ManagedBlocker() { | |
| @Override | |
| public boolean block() throws InterruptedException { | |
| waitFor(); | |
| return true; | |
| } | |
| @Override | |
| public boolean isReleasable() { | |
| return !isAlive(); | |
| } | |
| }); | |
| break; | |
| } catch (InterruptedException x) { | |
| interrupted = true; | |
| } | |
| } | |
| if (interrupted) { | |
| Thread.currentThread().interrupt(); | |
| } | |
| return this; | |
| } | |
| /** | |
| * Returns a ProcessHandle for the Process. | |
| * | |
| * {@code Process} objects returned by {@link ProcessBuilder#start()} and | |
| * {@link Runtime#exec} implement {@code toHandle} as the equivalent of | |
| * {@link ProcessHandle#of(long) ProcessHandle.of(pid)}. | |
| * | |
| * @implSpec | |
| * This implementation throws an instance of | |
| * {@link java.lang.UnsupportedOperationException} and performs no other action. | |
| * Subclasses should override this method to provide a ProcessHandle for the | |
| * process. The methods {@link #pid}, {@link #info}, {@link #children}, | |
| * and {@link #descendants}, unless overridden, operate on the ProcessHandle. | |
| * | |
| * @return Returns a ProcessHandle for the Process | |
| * @throws UnsupportedOperationException if the Process implementation | |
| * does not support this operation | |
| * @since 9 | |
| */ | |
| public ProcessHandle toHandle() { | |
| throw new UnsupportedOperationException(this.getClass() | |
| + ".toHandle() not supported"); | |
| } | |
| /** | |
| * Returns a snapshot of information about the process. | |
| * | |
| * <p> A {@link ProcessHandle.Info} instance has accessor methods | |
| * that return information about the process if it is available. | |
| * | |
| * @implSpec | |
| * This implementation returns information about the process as: | |
| * {@link #toHandle toHandle().info()}. | |
| * | |
| * @return a snapshot of information about the process, always non-null | |
| * @throws UnsupportedOperationException if the Process implementation | |
| * does not support this operation | |
| * @since 9 | |
| */ | |
| public ProcessHandle.Info info() { | |
| return toHandle().info(); | |
| } | |
| /** | |
| * Returns a snapshot of the direct children of the process. | |
| * The parent of a direct child process is the process. | |
| * Typically, a process that is {@linkplain #isAlive not alive} has no children. | |
| * <p> | |
| * <em>Note that processes are created and terminate asynchronously. | |
| * There is no guarantee that a process is {@linkplain #isAlive alive}. | |
| * </em> | |
| * | |
| * @implSpec | |
| * This implementation returns the direct children as: | |
| * {@link #toHandle toHandle().children()}. | |
| * | |
| * @return a sequential Stream of ProcessHandles for processes that are | |
| * direct children of the process | |
| * @throws UnsupportedOperationException if the Process implementation | |
| * does not support this operation | |
| * @since 9 | |
| */ | |
| public Stream<ProcessHandle> children() { | |
| return toHandle().children(); | |
| } | |
| /** | |
| * Returns a snapshot of the descendants of the process. | |
| * The descendants of a process are the children of the process | |
| * plus the descendants of those children, recursively. | |
| * Typically, a process that is {@linkplain #isAlive not alive} has no children. | |
| * <p> | |
| * <em>Note that processes are created and terminate asynchronously. | |
| * There is no guarantee that a process is {@linkplain #isAlive alive}. | |
| * </em> | |
| * | |
| * @implSpec | |
| * This implementation returns all children as: | |
| * {@link #toHandle toHandle().descendants()}. | |
| * | |
| * @return a sequential Stream of ProcessHandles for processes that | |
| * are descendants of the process | |
| * @throws UnsupportedOperationException if the Process implementation | |
| * does not support this operation | |
| * @since 9 | |
| */ | |
| public Stream<ProcessHandle> descendants() { | |
| return toHandle().descendants(); | |
| } | |
| /** | |
| * An input stream for a subprocess pipe that skips by reading bytes | |
| * instead of seeking, the underlying pipe does not support seek. | |
| */ | |
| static class PipeInputStream extends FileInputStream { | |
| PipeInputStream(FileDescriptor fd) { | |
| super(fd); | |
| } | |
| @Override | |
| public long skip(long n) throws IOException { | |
| long remaining = n; | |
| int nr; | |
| if (n <= 0) { | |
| return 0; | |
| } | |
| int size = (int)Math.min(2048, remaining); | |
| byte[] skipBuffer = new byte[size]; | |
| while (remaining > 0) { | |
| nr = read(skipBuffer, 0, (int)Math.min(size, remaining)); | |
| if (nr < 0) { | |
| break; | |
| } | |
| remaining -= nr; | |
| } | |
| return n - remaining; | |
| } | |
| @Override | |
| public int read() throws IOException { | |
| boolean attempted = Blocker.begin(); | |
| try { | |
| return super.read(); | |
| } finally { | |
| Blocker.end(attempted); | |
| } | |
| } | |
| @Override | |
| public int read(byte[] b) throws IOException { | |
| boolean attempted = Blocker.begin(); | |
| try { | |
| return super.read(b); | |
| } finally { | |
| Blocker.end(attempted); | |
| } | |
| } | |
| @Override | |
| public int read(byte[] b, int off, int len) throws IOException { | |
| boolean attempted = Blocker.begin(); | |
| try { | |
| return super.read(b, off, len); | |
| } finally { | |
| Blocker.end(attempted); | |
| } | |
| } | |
| } | |
| /** | |
| * An output stream for a subprocess pipe. | |
| */ | |
| static class PipeOutputStream extends FileOutputStream { | |
| PipeOutputStream(FileDescriptor fd) { | |
| super(fd); | |
| } | |
| @Override | |
| public void write(int b) throws IOException { | |
| boolean attempted = Blocker.begin(); | |
| try { | |
| super.write(b); | |
| } finally { | |
| Blocker.end(attempted); | |
| } | |
| } | |
| @Override | |
| public void write(byte[] b) throws IOException { | |
| boolean attempted = Blocker.begin(); | |
| try { | |
| super.write(b); | |
| } finally { | |
| Blocker.end(attempted); | |
| } | |
| } | |
| @Override | |
| public void write(byte[] b, int off, int len) throws IOException { | |
| boolean attempted = Blocker.begin(); | |
| try { | |
| super.write(b, off, len); | |
| } finally { | |
| Blocker.end(attempted); | |
| } | |
| } | |
| } | |
| /** | |
| * A nested class to delay looking up the Charset for the native encoding. | |
| */ | |
| private static class CharsetHolder { | |
| private static final Charset nativeCharset; | |
| static { | |
| Charset cs; | |
| try { | |
| cs = Charset.forName(StaticProperty.nativeEncoding()); | |
| } catch (UnsupportedCharsetException uce) { | |
| cs = Charset.defaultCharset(); | |
| } | |
| nativeCharset = cs; | |
| } | |
| /** | |
| * Charset for the native encoding or {@link Charset#defaultCharset(). | |
| */ | |
| static Charset nativeCharset() { | |
| return nativeCharset; | |
| } | |
| } | |
| } |