1

I'm trying to use ProcessBuilder for something as simple as an 'ls' command. I already read the issues about the process streams required to be consummed before the waitFor() call returns, but even with error redirected to output and consumming the stream, the process never returns.

public class ProcessTest {

    public static void main(String[] args) throws Exception {

        ProcessBuilder builder = new ProcessBuilder();
        builder.command("ls");

        builder.redirectErrorStream(true);

        Process process = builder.start();

        StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), System.out::println);

        Executors.newSingleThreadExecutor().submit(streamGobbler);

        int exitCode = process.waitFor();
    }

}

with StreamGlobber beeing a naive consummer :

    public class StreamGobbler implements Runnable {
    private InputStream inputStream;
    private Consumer<String> consumer;

    public StreamGobbler(InputStream inputStream, Consumer<String> consumer) {
        this.inputStream = inputStream;
        this.consumer = consumer;
    }

    @Override
    public void run() {
        new BufferedReader(new InputStreamReader(inputStream)).lines().forEach(consumer);
    }
}

can someone show me what's i'm going wrong here ?

Pierre Gayvallet
  • 2,933
  • 2
  • 23
  • 37

1 Answers1

3

The problem isn't with handling the external process, it's the incorrect use of the ExecutorService returned by Executors.newSingleThreadExecutor(). This method creates a thread running in the background, and because you never shut down this executor (which would stop the thread), this thread prevents the JVM from exiting.

If you want to, you can prove that the ls process exits by adding a line which prints out its exit code. Note however that this line might not appear at the bottom of the output.

To address this, we need to keep a reference to the ExecutorService created, and then shut it down when we don't need it any more. Try replacing

        Executors.newSingleThreadExecutor().submit(streamGobbler);

        int exitCode = process.waitFor();

with

        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(streamGobbler);

        int exitCode = process.waitFor();

        executor.shutdown();

I made this change to your code and it went from hanging to exiting cleanly.

Luke Woodward
  • 63,336
  • 16
  • 89
  • 104