Interface ProcessBuilder<O>

Type Parameters:
O - the output type
All Superinterfaces:
PipelineBuilder<O>
All Known Subinterfaces:
ProcessBuilder.Input<O>

public sealed interface ProcessBuilder<O> extends PipelineBuilder<O> permits ProcessBuilder.Input<O> (not exhaustive)
A builder for creating, running, and performing I/O with subprocesses.

The overall process follows this state diagram:

State machine diagram

Problems with the JDK ProcessBuilder

The JDK provides an interface to create and reap processes, which this API is based on. This API contains all of the pieces necessary to perform this function. However, there are many unstated requirements and caveats when using this API:
  • All I/O has to be performed in separate, individual threads to avoid various deadlocks
  • The ordering of process waiting and I/O is tricky to manage
  • Processes may need to be cleaned up on JDK exit
  • Error and output can be tricky to capture when they also need to be processed
The goal of this process API is to make it easy to create and manage processes, while also making it difficult to inadvertently cause deadlocks or other similar non-obvious problems.

Running processes

Trivial process execution can be initiated using the exec(...) and execToString(...) methods. To configure and create a single process beyond trivial cases, the full builder API must be used.

The builder API is used by calling newBuilder(...), chaining additional calls from the result of that method to configure the process, and finally calling PipelineBuilder.run() to start the process execution. Once PipelineBuilder.run() is called, the builder may no longer be used. Here's an example:

 
    String result = ProcessBuilder.newBuilder("echo", "hello world")
      .output().toSingleString(500)
      .run();
 
 
In this example, a new process builder which calls the program echo is created. A single argument (the string "hello world") is passed to the program. The output of the program is configured to be captured as a single string of up to 500 characters, and then the process is executed. Since the output was configured using PipelineBuilder.Output.toSingleString(int), the process execution returns a String. If, for example, the output is discarded, then PipelineBuilder.run() would return a Void (i.e., null).

Configuring builder parameters

Environment

When a new process builder is created, the environment of the current process is copied into the builder. This can be overridden by calling environment(Map) to replace the entire environment for the subprocess, or modifyEnvironment(Consumer) to use a handler callback (typically a lambda) to perform arbitrary manipulations of the environment map.

When creating a pipeline, the environment of the previous stage is used as the default environment for all subsequent stages.

Working directory

Process builders are initialized to use the working directory of the current process by default. This can be overridden by calling directory(Path). The given path must exist on the default filesystem.

Subsequent pipeline stages inherit the working directory of previous stages by default.

Arguments

The command arguments may be given when constructing the builder, or the arguments(...) methods may be used to configure the arguments separately.

Timeouts

It is possible to configure a soft exit timeout and a hard exit timeout. These timeouts are used to cause the process to exit in the case where it lingers after all I/O is complete. The soft timeout will request a "normal" termination, if it is supported by that platform (i.e. not Windows), if that time elapses after I/O is complete but before the process exits. The hard timeout will forcefully terminate the subprocess if the process lingers for the given duration after the soft timeout has been triggered.

Processing input and output

The methods input(), PipelineBuilder.output(), and PipelineBuilder.error() are used to configure the behavior of the process input, output, and error output, respectively. Each of these methods returns a view of this builder which has methods to configure that aspect of process I/O.

Input processing

Process input may be empty, inherited from the current process, transferred from a file, a reader, or a stream, generated from a string or a collection of strings, or produced via callback as characters or bytes.

When generating input as characters, the input character set is used. By default, the builder will use the native character set, but a specific character set can be configured by calling ProcessBuilder.Input.charset(Charset). These methods have no effect when generating byte input.

Output processing

In addition to determining whether and how process output is consumed, configuring the output handling strategy also affects the type of the value returned by the PipelineBuilder.run() method. In cases where the output type is impacted, the chaining method will return a view builder whose type will reflect the new output type. Therefore, the return value of each builder method should normally only be used to call the next builder method, and should not normally be saved or reused.

As with input, the native character set is used for output processing by default, but this can be configured as well.

Basic output targets

Process output can be discarded, inherited from the current process, transferred to a file (optionally in append mode), a writer, or a stream, consumed as bytes, characters, or lines.

The output can also be processed to produce a value of some type. The single string and list of strings processors are provided, but a custom processor which handles process output as bytes or characters may be provided. The processor returns an object of the desired type, and on success, that value is returned from PipelineBuilder.run().

It is also possible to configure the builder to capture the process output to include in any thrown exception. This is disabled by default but can be enabled by calling Output.gatherOnFail(true).

Multiple output handlers

It is also possible to split or "tee" a copy of the process output to one or more additional consumers. This can be useful, for example, to log the output of a process while also passing it on to another handler. The Output.copyAndXxx(...) methods work similarly to the primary output control methods, but also copy the data to the given handler.

Error processing

Error output may be processed similarly to how output is processed, with the exception that error output cannot be piped to another pipeline stage.

It is also possible to merge the error output of the process into its regular output by calling PipelineBuilder.Error.redirect(). The combined standard error and standard output will be merged and handled using the output handler. Note that using this mode will prevent error output from being captured in the case of an exception.

Process pipelines

Process output can be configured to feed directly into the input of another process by calling one of the Output.pipeTo(...) methods. These methods return a new process builder to configure the subsequent pipeline stage. Once the pipeTo(...) method is called, the builder for the original process can no longer be modified. Thus, the output stage should generally be configured last.

Since the new process builder represents a subsequent pipeline stage whose input always comes from the previous stage, there is no way to configure input handling for subsequent pipeline stages. Additionally, timeouts apply to the entire pipeline, not to individual stages; thus, timeouts must be configured only on the first pipeline stage. Other parameters, such as the working directory, environment, etc. may be configured on a per-stage basis and normally inherit their settings from the previous stage.

Exit code validation

By default, if the process exits with a nonzero exit code it is assumed to have failed, which will trigger an exception to be thrown that details the problem. A custom exit code checker can be injected which can be used to capture and save the exit code and/or establish an alternative validation policy for exit codes. The checker is a predicate which returns true if the exit code is successful or false if it should result in a failure exception being thrown.
  • Field Details

    • DEFAULT_SOFT_TIMEOUT

      static final Duration DEFAULT_SOFT_TIMEOUT
      The default soft timeout duration.
    • DEFAULT_HARD_TIMEOUT

      static final Duration DEFAULT_HARD_TIMEOUT
      The default hard timeout duration.
  • Method Details

    • exec

      static void exec(Path command, String... args)
      Execute the given command and arguments, discarding the result.
      Parameters:
      command - the command to execute (must not be null)
      args - the arguments (must not be null)
    • exec

      static void exec(Path command, List<String> args)
      Execute the given command and arguments, discarding the result.
      Parameters:
      command - the command to execute (must not be null)
      args - the arguments (must not be null)
    • exec

      static void exec(String command, String... args)
      Execute the given command and arguments, discarding the result.
      Parameters:
      command - the command to execute (must not be null)
      args - the arguments (must not be null)
    • exec

      static void exec(String command, List<String> args)
      Execute the given command and arguments, discarding the result.
      Parameters:
      command - the command to execute (must not be null)
      args - the arguments (must not be null)
    • execToString

      static String execToString(Path command, String... args)
      Execute the given command and arguments, returning the result as a single string.
      Parameters:
      command - the command to execute (must not be null)
      args - the arguments (must not be null)
      Returns:
      the result as a single string (not null)
    • execToString

      static String execToString(Path command, List<String> args)
      Execute the given command and arguments, returning the result as a single string.
      Parameters:
      command - the command to execute (must not be null)
      args - the arguments (must not be null)
      Returns:
      the result as a single string (not null)
    • execToString

      static String execToString(String command, String... args)
      Execute the given command and arguments, returning the result as a single string.
      Parameters:
      command - the command to execute (must not be null)
      args - the arguments (must not be null)
      Returns:
      the result as a single string (not null)
    • execToString

      static String execToString(String command, List<String> args)
      Execute the given command and arguments, returning the result as a single string.
      Parameters:
      command - the command to execute (must not be null)
      args - the arguments (must not be null)
      Returns:
      the result as a single string (not null)
    • newBuilder

      static ProcessBuilder<Void> newBuilder(Path command)
      Create a new process builder.
      Parameters:
      command - the command to execute (must not be null)
      Returns:
      the new process builder (not null)
    • newBuilder

      static ProcessBuilder<Void> newBuilder(Path command, List<String> args)
      Create a new process builder.
      Parameters:
      command - the command to execute (must not be null)
      args - the arguments (must not be null)
      Returns:
      the new process builder (not null)
    • newBuilder

      static ProcessBuilder<Void> newBuilder(Path command, String... args)
      Create a new process builder.
      Parameters:
      command - the command to execute (must not be null)
      args - the arguments (must not be null)
      Returns:
      the new process builder (not null)
    • newBuilder

      static ProcessBuilder<Void> newBuilder(String command)
      Create a new process builder.
      Parameters:
      command - the command to execute (must not be null)
      Returns:
      the new process builder (not null)
    • newBuilder

      static ProcessBuilder<Void> newBuilder(String command, List<String> args)
      Create a new process builder.
      Parameters:
      command - the command to execute (must not be null)
      args - the arguments (must not be null)
      Returns:
      the new process builder (not null)
    • newBuilder

      static ProcessBuilder<Void> newBuilder(String command, String... args)
      Create a new process builder.
      Parameters:
      command - the command to execute (must not be null)
      args - the arguments (must not be null)
      Returns:
      the new process builder (not null)
    • arguments

      ProcessBuilder<O> arguments(List<String> command)
      Description copied from interface: PipelineBuilder
      Set the arguments for this process execution.
      Specified by:
      arguments in interface PipelineBuilder<O>
      Parameters:
      command - the arguments for this process execution (must not be null)
      Returns:
      this builder
    • arguments

      default ProcessBuilder<O> arguments(String... command)
      Description copied from interface: PipelineBuilder
      Set the arguments for this process execution.
      Specified by:
      arguments in interface PipelineBuilder<O>
      Parameters:
      command - the arguments for this process execution (must not be null)
      Returns:
      this builder
    • specialQuoting

      ProcessBuilder<O> specialQuoting(boolean specialQuoting)
      Description copied from interface: PipelineBuilder
      Enable special quoting for batch scripts on Windows. Note that this requires the batch script to use a specific form of argument expansion.
      Specified by:
      specialQuoting in interface PipelineBuilder<O>
      Parameters:
      specialQuoting - true to enable special quoting, or false to use default quoting
      Returns:
      this builder
    • directory

      ProcessBuilder<O> directory(Path directory)
      Description copied from interface: PipelineBuilder
      Set the working directory for this process execution.
      Specified by:
      directory in interface PipelineBuilder<O>
      Parameters:
      directory - the working directory (must not be null)
      Returns:
      this builder
    • environment

      default ProcessBuilder<O> environment(Map<String,String> newEnvironment)
      Description copied from interface: PipelineBuilder
      Set the environment for this process execution, overriding the inherited environment.
      Specified by:
      environment in interface PipelineBuilder<O>
      Parameters:
      newEnvironment - the new environment (must not be null)
      Returns:
      this builder
    • modifyEnvironment

      ProcessBuilder<O> modifyEnvironment(Consumer<Map<String,String>> action)
      Description copied from interface: PipelineBuilder
      Allow the given action to modify the environment of the subprocess. The action is provided a mutable map which can be arbitrarily modified for the duration of the callback. Modifying the map after the callback returns will result in undefined behavior and should not be attempted.
      Specified by:
      modifyEnvironment in interface PipelineBuilder<O>
      Parameters:
      action - the action (must not be null)
      Returns:
      this builder
    • exitCodeChecker

      ProcessBuilder<O> exitCodeChecker(IntPredicate checker)
      Description copied from interface: PipelineBuilder
      Add a failure exit code checker. The checker returns true if the process execution should fail for this exit code. If so, a ProcessException will be constructed and thrown. The exception will generally include some or all of the data received from the error stream of the subprocess.
      Specified by:
      exitCodeChecker in interface PipelineBuilder<O>
      Parameters:
      checker - the exit code checker (must not be null)
      Returns:
      this builder
    • whileRunning

      Description copied from interface: PipelineBuilder
      Set the while-running process handler.
      Specified by:
      whileRunning in interface PipelineBuilder<O>
      Parameters:
      action - the action to run (must not be null)
      Returns:
      this builder
    • input

      Configure the input handling of the process.
      Returns:
      the input configuration view of this builder (not null)
    • softExitTimeout

      ProcessBuilder<O> softExitTimeout(Duration duration)
      Set the soft exit timeout. This is the time to wait after all I/O is processed before gracefully terminating the subprocess.
      Parameters:
      duration - the soft exit timeout, or null for no timeout
      Returns:
      this builder
    • hardExitTimeout

      ProcessBuilder<O> hardExitTimeout(Duration duration)
      Set the hard exit timeout. This is the time to wait after all I/O is processed and the soft timeout has elapsed before forcefully terminating the subprocess.
      Parameters:
      duration - the hard exit timeout, or null for no timeout
      Returns:
      this builder