Class ProcessExecutor


  • public class ProcessExecutor
    extends java.lang.Object
    Helper for executing sub processes.

    It's implemented as a wrapper of ProcessBuilder complementing it with additional features such as:

    • Handling process streams (copied from Commons Exec library).
    • Destroying process on VM exit (copied from Commons Exec library).
    • Checking process exit code.
    • Setting a timeout for running the process and automatically stopping it in case of timeout.
    • Either waiting for the process to finish (execute()) or returning a Future (start().
    • Reading the process output stream into a buffer (readOutput(boolean), ProcessResult).

    The default configuration for executing a process is following:

    See Also:
    ProcessResult
    • Field Detail

      • log

        private static final org.slf4j.Logger log
      • IS_OS_WINDOWS

        private static final boolean IS_OS_WINDOWS
      • DEFAULT_EXIT_VALUES

        public static final java.lang.Integer[] DEFAULT_EXIT_VALUES
      • NORMAL_EXIT_VALUE

        private static final java.lang.Integer NORMAL_EXIT_VALUE
      • DEFAULT_REDIRECT_ERROR_STREAM

        public static final boolean DEFAULT_REDIRECT_ERROR_STREAM
        See Also:
        Constant Field Values
      • builder

        private final java.lang.ProcessBuilder builder
        Process builder used by this executor.
      • environment

        private final java.util.Map<java.lang.String,​java.lang.String> environment
        Environment variables which are added (removed in case of null values) to the process being started.
      • allowedExitValues

        private java.util.Set<java.lang.Integer> allowedExitValues
        Set of accepted exit codes or null if all exit codes are allowed.
      • timeout

        private java.lang.Long timeout
        Timeout for running a process. If the process is running too long a TimeoutException is thrown and the process is destroyed.
      • timeoutUnit

        private java.util.concurrent.TimeUnit timeoutUnit
      • stopper

        private ProcessStopper stopper
        Helper for stopping the process in case of timeout or cancellation.
      • streams

        private ExecuteStreamHandler streams
        Process stream Handler (copied from Commons Exec library). If null streams are not handled.
      • closeTimeout

        private java.lang.Long closeTimeout
        Timeout for closing process' standard streams. In case this timeout is reached we just log a warning but don't throw an error.
      • closeTimeoutUnit

        private java.util.concurrent.TimeUnit closeTimeoutUnit
      • readOutput

        private boolean readOutput
        true if the process output should be read to a buffer and returned by ProcessResult.output().
      • messageLogger

        private MessageLogger messageLogger
        Helper for logging messages about starting and waiting for the processes.
    • Constructor Detail

      • ProcessExecutor

        public ProcessExecutor()
        Creates new ProcessExecutor instance.
      • ProcessExecutor

        public ProcessExecutor​(java.util.List<java.lang.String> command)
        Creates new ProcessExecutor instance for the given program and its arguments.
        Parameters:
        command - The list containing the program and its arguments.
      • ProcessExecutor

        public ProcessExecutor​(java.lang.String... command)
        Creates new ProcessExecutor instance for the given program and its arguments.
        Parameters:
        command - A string array containing the program and its arguments.
      • ProcessExecutor

        public ProcessExecutor​(java.lang.Iterable<java.lang.String> command)
        Creates new ProcessExecutor instance for the given program and its arguments.
        Parameters:
        command - The iterable containing the program and its arguments.
        Since:
        1.8
    • Method Detail

      • getCommand

        public java.util.List<java.lang.String> getCommand()
        Returns this process executor's operating system program and arguments. The returned list is a copy.
        Returns:
        this process executor's program and its arguments (not null).
      • command

        public ProcessExecutor command​(java.util.List<java.lang.String> command)
        Sets the program and its arguments which are being executed.
        Parameters:
        command - The list containing the program and its arguments.
        Returns:
        This process executor.
      • command

        public ProcessExecutor command​(java.lang.String... command)
        Sets the program and its arguments which are being executed.
        Parameters:
        command - A string array containing the program and its arguments.
        Returns:
        This process executor.
      • command

        public ProcessExecutor command​(java.lang.Iterable<java.lang.String> command)
        Sets the program and its arguments which are being executed.
        Parameters:
        command - The iterable containing the program and its arguments.
        Returns:
        This process executor.
        Since:
        1.8
      • commandSplit

        public ProcessExecutor commandSplit​(java.lang.String commandWithArgs)
        Splits string by spaces and passes it to command(String...)
        NB: this method do not handle whitespace escaping, "mkdir new\ folder" would be interpreted as {"mkdir", "new\", "folder"} command.
        Parameters:
        commandWithArgs - A string array containing the program and its arguments.
        Returns:
        This process executor.
      • getDirectory

        public java.io.File getDirectory()
        Returns this process executor's working directory. Subprocesses subsequently started by this object will use this as their working directory. The returned value may be null -- this means to use the working directory of the current Java process, usually the directory named by the system property user.dir, as the working directory of the child process.
        Returns:
        this process executor's working directory
      • directory

        public ProcessExecutor directory​(java.io.File directory)
        Sets this working directory for the process being executed. The argument may be null -- this means to use the working directory of the current Java process, usually the directory named by the system property user.dir, as the working directory of the child process.
        Parameters:
        directory - The new working directory
        Returns:
        This process executor.
      • getEnvironment

        public java.util.Map<java.lang.String,​java.lang.String> getEnvironment()
        Returns this process executor's additional environment variables. The returned value is not a copy.
        Returns:
        this process executor's environment variables (not null).
      • environment

        public ProcessExecutor environment​(java.util.Map<java.lang.String,​java.lang.String> env)
        Adds additional environment variables for the process being executed.
        Parameters:
        env - environment variables added to the process being executed.
        Returns:
        This process executor.
      • environment

        public ProcessExecutor environment​(java.lang.String name,
                                           java.lang.String value)
        Adds a single additional environment variable for the process being executed.
        Parameters:
        name - name of the environment variable added to the process being executed.
        value - value of the environment variable added to the process being executed.
        Returns:
        This process executor.
        Since:
        1.7
      • redirectErrorStream

        public ProcessExecutor redirectErrorStream​(boolean redirectErrorStream)
        Sets this process executor's redirectErrorStream property.

        If this property is true, then any error output generated by subprocesses will be merged with the standard output. This makes it easier to correlate error messages with the corresponding output. The initial value is true.

        Parameters:
        redirectErrorStream - The new property value
        Returns:
        This process executor.
      • exitValueAny

        public ProcessExecutor exitValueAny()
        Allows any exit value for the process being executed.
        Returns:
        This process executor.
      • exitValueNormal

        public ProcessExecutor exitValueNormal()
        Allows only 0 as the exit value for the process being executed.
        Returns:
        This process executor.
      • exitValue

        public ProcessExecutor exitValue​(java.lang.Integer exitValue)
        Sets the allowed exit value for the process being executed.
        Parameters:
        exitValue - single exit value or null if all exit values are allowed.
        Returns:
        This process executor.
      • exitValues

        public ProcessExecutor exitValues​(java.lang.Integer... exitValues)
        Sets the allowed exit values for the process being executed.
        Parameters:
        exitValues - set of exit values or null if all exit values are allowed.
        Returns:
        This process executor.
      • exitValues

        public ProcessExecutor exitValues​(int[] exitValues)
        Sets the allowed exit values for the process being executed.
        Parameters:
        exitValues - set of exit values or null if all exit values are allowed.
        Returns:
        This process executor.
      • timeout

        public ProcessExecutor timeout​(long timeout,
                                       java.util.concurrent.TimeUnit unit)
        Sets a timeout for the process being executed. When this timeout is reached a TimeoutException is thrown and the process is destroyed. This only applies to execute methods not start methods.
        Parameters:
        timeout - timeout for running a process.
        unit - the time unit of the timeout
        Returns:
        This process executor.
      • stopper

        public ProcessExecutor stopper​(ProcessStopper stopper)
        Sets the helper for stopping the process in case of timeout or cancellation.

        By default DestroyProcessStopper is used which just invokes Process.destroy().

        Parameters:
        stopper - helper for stopping the process (null means NopProcessStopper - process is not stopped).
        Returns:
        This process executor.
      • streams

        public ExecuteStreamHandler streams()
        Returns:
        current stream handler for the process being executed.
      • streams

        public ProcessExecutor streams​(ExecuteStreamHandler streams)
        Sets a stream handler for the process being executed. This will overwrite any stream redirection that was previously set to use the provided handler.
        Parameters:
        streams - the stream handler
        Returns:
        This process executor.
      • closeTimeout

        public ProcessExecutor closeTimeout​(long timeout,
                                            java.util.concurrent.TimeUnit unit)
        Sets a timeout for closing standard streams of the process being executed. When this timeout is reached we log a warning but consider that the process has finished. We also flush the streams so that all output read so far is available.

        This can be used on Windows in case a process exits quickly but closing the streams blocks forever.

        Closing timeout must fit into the general execution timeout (see timeout(long, TimeUnit)). By default there's no closing timeout.

        Parameters:
        timeout - timeout for closing streams of a process.
        unit - the time unit of the timeout
        Returns:
        This process executor.
      • redirectInput

        public ProcessExecutor redirectInput​(java.io.InputStream input)
        Sets the input stream to redirect to the process' input stream. If this method is invoked multiple times each call overwrites the previous.
        Parameters:
        input - input stream that will be written to the process input stream (null means nothing will be written to the process input stream).
        Returns:
        This process executor.
      • redirectOutput

        public ProcessExecutor redirectOutput​(java.io.OutputStream output)
        Redirects the process' output stream to given output stream. If this method is invoked multiple times each call overwrites the previous. Use redirectOutputAlsoTo(OutputStream) if you want to redirect the output to multiple streams.
        Parameters:
        output - output stream where the process output is redirected to (null means NullOutputStream which acts like a /dev/null).
        Returns:
        This process executor.
      • redirectError

        public ProcessExecutor redirectError​(java.io.OutputStream output)
        Redirects the process' error stream to given output stream. If this method is invoked multiple times each call overwrites the previous. Use redirectErrorAlsoTo(OutputStream) if you want to redirect the error to multiple streams.

        Calling this method automatically disables merging the process error stream to its output stream.

        Parameters:
        output - output stream where the process error is redirected to (null means NullOutputStream which acts like a /dev/null).
        Returns:
        This process executor.
      • redirectOutputAlsoTo

        public ProcessExecutor redirectOutputAlsoTo​(java.io.OutputStream output)
        Redirects the process' output stream also to a given output stream. This method can be used to redirect output to multiple streams.
        Parameters:
        output - the stream to redirect this output to
        Returns:
        This process executor.
      • redirectErrorAlsoTo

        public ProcessExecutor redirectErrorAlsoTo​(java.io.OutputStream output)
        Redirects the process' error stream also to a given output stream. This method can be used to redirect error to multiple streams.

        Calling this method automatically disables merging the process error stream to its output stream.

        Parameters:
        output - the output stream to redirect the error stream to
        Returns:
        This process executor.
      • pumps

        public PumpStreamHandler pumps()
        Returns:
        current PumpStreamHandler (maybe null).
        Throws:
        java.lang.IllegalStateException - if the current stream handler is not an instance of PumpStreamHandler.
        See Also:
        streams()
      • redirectOutputAlsoTo

        private static PumpStreamHandler redirectOutputAlsoTo​(PumpStreamHandler pumps,
                                                              java.io.OutputStream output)
        Redirects the process' output stream also to a given output stream.
        Returns:
        new stream handler created.
      • redirectErrorAlsoTo

        private static PumpStreamHandler redirectErrorAlsoTo​(PumpStreamHandler pumps,
                                                             java.io.OutputStream output)
        Redirects the process' error stream also to a given output stream.
        Returns:
        new stream handler created.
      • readOutput

        public ProcessExecutor readOutput​(boolean readOutput)
        Sets this process executor's readOutput property.

        If this property is true, the process output should be read to a buffer and returned by ProcessResult.output(). The initial value is false.

        Parameters:
        readOutput - The new property value
        Returns:
        This process executor.
      • validateStreams

        private void validateStreams​(ExecuteStreamHandler streams,
                                     boolean readOutput)
        Validates that if readOutput is true the output could be read with the given ExecuteStreamHandler instance.
      • debug

        public ProcessExecutor debug​(org.slf4j.Logger log)
        Logs the process' output to a given Logger with debug level.
        Parameters:
        log - the logger to process the output to
        Returns:
        This process executor.
      • info

        public ProcessExecutor info​(java.lang.String name)
        Logs the process' output to a Logger with given name using info level.
        Parameters:
        name - the name of the logger to process the output to
        Returns:
        This process executor.
      • debug

        public ProcessExecutor debug​(java.lang.String name)
        Logs the process' output to a Logger with given name using debug level.
        Parameters:
        name - the name of the logger to process the output to
        Returns:
        This process executor.
      • redirectOutputAsInfo

        public ProcessExecutor redirectOutputAsInfo​(org.slf4j.Logger log)
        Logs the process' output to a given Logger with info level.
        Parameters:
        log - the logger to output the message to
        Returns:
        This process executor.
      • redirectOutputAsDebug

        public ProcessExecutor redirectOutputAsDebug​(org.slf4j.Logger log)
        Logs the process' output to a given Logger with debug level.
        Parameters:
        log - the logger to output the message to
        Returns:
        This process executor.
      • redirectOutputAsInfo

        public ProcessExecutor redirectOutputAsInfo​(java.lang.String name)
        Logs the process' output to a Logger with given name using info level.
        Parameters:
        name - the name of the logger to log to
        Returns:
        This process executor.
      • redirectOutputAsDebug

        public ProcessExecutor redirectOutputAsDebug​(java.lang.String name)
        Logs the process' output to a Logger with given name using debug level.
        Parameters:
        name - the name of the logger to process output to
        Returns:
        This process executor.
      • redirectErrorAsInfo

        public ProcessExecutor redirectErrorAsInfo​(org.slf4j.Logger log)
        Logs the process' error to a given Logger with info level.
        Parameters:
        log - the logger to process output to
        Returns:
        This process executor.
      • redirectErrorAsDebug

        public ProcessExecutor redirectErrorAsDebug​(org.slf4j.Logger log)
        Logs the process' error to a given Logger with debug level.
        Parameters:
        log - the logger to process the error to
        Returns:
        This process executor.
      • redirectErrorAsInfo

        public ProcessExecutor redirectErrorAsInfo​(java.lang.String name)
        Logs the process' error to a Logger with given name using info level.
        Parameters:
        name - the name of the logger to process the error to
        Returns:
        This process executor.
      • redirectErrorAsDebug

        public ProcessExecutor redirectErrorAsDebug​(java.lang.String name)
        Logs the process' error to a Logger with given name using debug level.
        Parameters:
        name - the name of the logger to process the error to
        Returns:
        This process executor.
      • getCallerLogger

        private org.slf4j.Logger getCallerLogger​(java.lang.String name)
        Creates a Logger for the ProcessExecutor's caller class.
        Parameters:
        name - name of the logger.
        Returns:
        SLF4J Logger instance.
      • addDestroyer

        public ProcessExecutor addDestroyer​(ProcessDestroyer destroyer)
        Adds a process destroyer to be notified when the process starts and stops.
        Parameters:
        destroyer - helper for destroying all processes on certain event such as VM exit (not null).
        Returns:
        This process executor.
      • destroyer

        public ProcessExecutor destroyer​(ProcessDestroyer destroyer)
        Sets the process destroyer to be notified when the process starts and stops.

        This methods always removes any other ProcessDestroyer registered. Use addDestroyer(ProcessDestroyer) to keep the existing ones.

        Parameters:
        destroyer - helper for destroying all processes on certain event such as VM exit (maybe null).
        Returns:
        This process executor.
      • destroyOnExit

        public ProcessExecutor destroyOnExit()
        Sets the started process to be destroyed on VM exit (shutdown hooks are executed). If this VM gets killed the started process may not get destroyed.

        To undo this command call destroyer(null).

        Returns:
        This process executor.
      • listener

        public ProcessExecutor listener​(ProcessListener listener)
        Unregister all existing process event handlers and register new one.
        Parameters:
        listener - process event handler to be set (maybe null).
        Returns:
        This process executor.
      • addListener

        public ProcessExecutor addListener​(ProcessListener listener)
        Register new process event handler.
        Parameters:
        listener - process event handler to be added.
        Returns:
        This process executor.
      • removeListener

        public ProcessExecutor removeListener​(ProcessListener listener)
        Unregister existing process event handler.
        Parameters:
        listener - process event handler to be removed.
        Returns:
        This process executor.
      • removeListeners

        public ProcessExecutor removeListeners​(java.lang.Class<? extends ProcessListener> listenerType)
        Unregister existing process event handlers of given type or its sub-types.
        Parameters:
        listenerType - process event handler type.
        Returns:
        This process executor.
      • clearListeners

        public ProcessExecutor clearListeners()
        Unregister all existing process event handlers.
        Returns:
        This process executor.
      • setMessageLogger

        public ProcessExecutor setMessageLogger​(MessageLogger messageLogger)
        Changes how most common messages about starting and waiting for processes are actually logged. By default MessageLoggers.DEBUG is used. However if someone is executing a process every second MessageLoggers.TRACE may be used e.g.
        Parameters:
        messageLogger - message logger for certain level.
        Returns:
        This process executor.
      • execute

        public ProcessResult execute()
                              throws java.io.IOException,
                                     java.lang.InterruptedException,
                                     java.util.concurrent.TimeoutException,
                                     InvalidExitValueException
        Executes the sub process. This method waits until the process exits, a timeout occurs or the caller thread gets interrupted. In the latter cases the process gets destroyed as well.
        Returns:
        exit code of the finished process.
        Throws:
        java.io.IOException - an error occurred when process was started or stopped.
        java.lang.InterruptedException - this thread was interrupted.
        java.util.concurrent.TimeoutException - timeout set by timeout(long, TimeUnit) was reached.
        InvalidExitValueException - if invalid exit value was returned (@see exitValues(Integer...)).
      • checkExitValue

        public void checkExitValue​(ProcessResult result)
                            throws InvalidExitValueException
        Check the exit value of given process result. This can be used by unit tests.
        Parameters:
        result - process result which maybe constructed by a unit test.
        Throws:
        InvalidExitValueException - if the given exit value was rejected.
      • start

        public StartedProcess start()
                             throws java.io.IOException
        Start the sub process. This method does not wait until the process exits. Value passed to timeout(long, TimeUnit) is ignored. Use Future.get() to wait for the process to finish. Invoke future.cancel(true); to destroy the process.
        Returns:
        Future representing the exit value of the finished process.
        Throws:
        java.io.IOException - an error occurred when process was started.
      • startInternal

        protected final WaitForProcess startInternal()
                                              throws java.io.IOException
        Start the process and its stream handlers.
        Returns:
        process the started process.
        Throws:
        java.io.IOException - the process or its stream handlers couldn't start (in the latter case we also destroy the process).
      • getAttributes

        private ProcessAttributes getAttributes()
        Capture a snapshot of this process executor's main state.
      • invokeStart

        private java.lang.Process invokeStart()
                                       throws java.io.IOException
        Throws:
        java.io.IOException
      • getExecutingLogMessage

        private java.lang.String getExecutingLogMessage()
      • getExecutingErrorMessage

        private java.lang.String getExecutingErrorMessage()
      • getExecutingMessageParams

        private java.lang.String getExecutingMessageParams()
      • waitFor

        private ProcessResult waitFor​(WaitForProcess task)
                               throws java.io.IOException,
                                      java.lang.InterruptedException,
                                      java.util.concurrent.TimeoutException
        Wait until the process stops, a timeout occurs and the caller thread gets interrupted. In the latter cases the process gets destroyed as well.
        Throws:
        java.io.IOException
        java.lang.InterruptedException
        java.util.concurrent.TimeoutException
      • newExecutor

        private java.util.concurrent.ExecutorService newExecutor​(WaitForProcess task)
      • newExecutor

        protected java.util.concurrent.ExecutorService newExecutor​(java.lang.String processName)
      • invokeSubmit

        protected <T> java.util.concurrent.Future<T> invokeSubmit​(java.util.concurrent.ExecutorService executor,
                                                                  java.util.concurrent.Callable<T> task)
        Override this to customize how the waiting task is started in the background.
        Type Parameters:
        T - the type of the task
        Parameters:
        executor - the executor service to submit the task on
        task - the task to be submitted
        Returns:
        the future of the task
      • wrapTask

        protected <T> java.util.concurrent.Callable<T> wrapTask​(java.util.concurrent.Callable<T> task)
        Override this to customize how the background task is created.
        Type Parameters:
        T - the type of the Task
        Parameters:
        task - the Task to be wrapped
        Returns:
        the wrapped task
      • newTimeoutException

        private java.util.concurrent.TimeoutException newTimeoutException​(long timeout,
                                                                          java.util.concurrent.TimeUnit unit,
                                                                          WaitForProcess task)
      • getUnitsAsString

        private static java.lang.String getUnitsAsString​(long d,
                                                         java.util.concurrent.TimeUnit unit)
      • getExitCodeOrNull

        private static java.lang.Integer getExitCodeOrNull​(java.lang.Process process)
      • applyEnvironment

        private void applyEnvironment()
      • fixArguments

        private static java.util.List<java.lang.String> fixArguments​(java.util.List<java.lang.String> command)
        Fixes the command line arguments on Windows by replacing empty arguments with "". Otherwise these arguments would be just skipped.