Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java ProcessBuilder: Resultant Process Hangs

I've been trying to use Java's ProcessBuilder to launch an application in Linux that should run "long-term". The way this program runs is to launch a command (in this case, I am launching a media playback application), allow it to run, and check to ensure that it hasn't crashed. For instance, check to see if the PID is still active, and then relaunch the process, if it has died.

The problem I'm getting right now is that the PID remains alive in the system, but the GUI for the application hangs. I tried shifting the ProcessBuilder(cmd).start() into a separate thread, but that doesn't seem to be solving anything, as I hoped it would have.

Basically the result is that, to the user, the program APPEARS to have crashed, but killing the Java process that drives the ProcessBuilder.start() Process actually allows the created Process to resume its normal behavior. This means that something in the Java application is interfering with the spawned Process, but I have absolutely no idea what, at this point. (Hence why I tried separating it into another thread, which didn't seem to resolve anything)

If anyone has any input/thoughts, please let me know, as I can't for the life of me think of how to solve this problem.

Edit: I have no concern over the I/O stream created from the Process, and have thus taken no steps to deal with that--could this cause a hang in the Process itself?

like image 742
Matt D Avatar asked Jul 19 '10 21:07

Matt D


4 Answers

If the process writes to stderr or stdout, and you're not reading it - it will just "hang" , blocking when writing to stdout/err. Either redirect stdout/err to /dev/null using a shell or merge stdout/err with redirectErrorStream(true) and spawn another thread that reads from stdout of the process

like image 93
nos Avatar answered Nov 17 '22 01:11

nos


You want the trick?

Don't start your process from ProcessBuilder.start(). Don't try to mess with stream redirection/consumption from Java (especially if you give no s**t about it ; )

Use ProcessBuilder.start() to start a little shell script that gobbles all the input/output streams.

Something like that:

#!/bin/bash

nohup $1 >/dev/null 2>error.log &

That is: if you don't care about stdout and still want to log stderr (do you?) to a file (error.log here).

If you don't even care about stderr, just redirect it to stdout:

#!/bin/bash

nohup $1 >/dev/null 2>1 &

And you call that tiny script from Java, giving it as an argument the name of the process you want to run.

If a process running on Linux that is redirecting both stdout and stderr to /dev/null still produce anything then you've got a broken, non-compliant, Linux install ;)

In other word: the above Just Works [TM] and get rid of the problematic "you need to consume the streams in this and that order bla bla bla Java-specific non-sense".

like image 44
SyntaxT3rr0r Avatar answered Nov 17 '22 01:11

SyntaxT3rr0r


The thread running the process may block if it does not handle the output. This can be done by spawning a new thread that reads the output of the process.

    final ProcessBuilder builder = new ProcessBuilder("script")
                    .redirectErrorStream(true)
                    .directory(workDirectory);

    final Process process = builder.start();
    final StringWriter writer = new StringWriter();

    new Thread(new Runnable() {
        public void run() {
            IOUtils.copy(process.getInputStream(), writer);
        }
    }).start();

    final int exitValue = process.waitFor();
    final String processOutput = writer.toString();
like image 11
Hector Avatar answered Nov 17 '22 01:11

Hector


Just stumbled on this after I had a similar issue. Agreeing with nos, you need to handle the output. I had something like this:

ProcessBuilder myProc2 = new ProcessBuilder(command);
final Process process = myProc2.start();

and it was working great. The spawned process even did output some output but not much. When I started to output a lot more, it appeared my process wasn't even getting launched anymore. I updated to this:

ProcessBuilder myProc2 = new ProcessBuilder(command);
myProc2.redirectErrorStream(true);        
final Process process = myProc2.start();
InputStream myIS = process.getInputStream();
String tempOut = convertStreamToStr(myIS);

and it started working again. (Refer to this link for convertStreamToStr() code)

like image 7
user320781 Avatar answered Nov 17 '22 02:11

user320781