Using a ProcessBuilder to Execute an OS Level Command and Properly Read the Exit Code from a Java Process

There are many a situation where a developer will want to execute an OS level command in another process and read not only the standard out (stdout), and standard error (sterr), but also the exit code returned from the process.

To do so, utilize the ProcessBuilder class along with a helper class (ProcessWrapper) that will provide the ability to set a timeout for the process and read the exit code in a separate thread.

Following is an example and the ProcessWrapper class with an explaination:

import java.io.IOException;

public class ProcessBuilderTest {

    /**
     * Time in milliseconds that we will wait for the process to complete before
     * timing out and killing it.
     */
    private static final long fTimeout = 10000;

    public static void main(String[] args) {

        String[] commandArray = { “some”, “command” };

        ProcessBuilder processBuilder = new ProcessBuilder(commandArray);

        Process process = null;
        try {
            process = processBuilder.start();
        } catch (IOException e) {
            // TODO: Error handling, logging
        }

        // ——————————————————————–
        // Read the exit code from the fProcess
        ProcessWrapper processWrapper = new ProcessWrapper(process);
        processWrapper.start();

        int exitCode = 0;
        try {
            processWrapper.join(fTimeout);

            // Check for an exit code from the process
            if (processWrapper.getfExitCode() != null) {
                exitCode = processWrapper.getfExitCode();
            } else {
                // Set our exit code to 1
                exitCode = 1;
                process.destroy();
            }
        } catch (InterruptedException e) {
            processWrapper.interrupt();
            Thread.currentThread().interrupt();
            process.destroy();
        }

        System.out.print(“exitCode = ” + exitCode);
    }

    public static class ProcessWrapper extends Thread {

        private final Process fProcess;

        private Integer fExitCode;

        public Integer getfExitCode() {
            return fExitCode;
        }

        public ProcessWrapper(Process fProcess) {
            this.fProcess = fProcess;
        }

        public void run() {
            try {
                fExitCode = fProcess.waitFor();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

In the ProcessBuilderTest main() method the command to be executed is build in the commandArray String[].

A ProcessBuilder instance is created and passed the command to execute. Then a Process instance providing a reference to the process running on the OS is instantiated via the ProcessBuilder.start() command.

Here is where the wrapper class that helps us to read the exit code comes in. A ProcessWrapper is instantiated and passed a reference to the process on construction, and then the thread started with the .start() invocation.

Then processWrapper.join(fTimeout) is invoked telling the current thread to wait for the completion of the processWrapper thread, or interrupt it (timeout) after the value of fTimeout (in this case 10 seconds).

If the processWrapper thread completes before the timeout the main thread invokes processWrapper.getfExitCode(), to acquire the exit code from the process. If not, the process is killed and we pass an exitCode of 1 to indicate abnormal completion.

Attached is a .zip of the java classes for reference and testing.

Leave a Reply