Tutorial :Java ProcessBuilder: Resultant Process Hangs



Question:

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?


Solution:1

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


Solution:2

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".


Solution:3

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();  


Solution:4

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: http://singztechmusings.wordpress.com/2011/06/21/getting-started-with-javas-processbuilder-a-sample-utility-class-to-interact-with-linux-from-java-program/)


Solution:5

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?

If you don't read the output streams created by the process then it is possible that the application will block once the application's buffers are full. I've never seen this happen on Linux (although I'm not saying that it doesn't) but I have seen this exact problem on Windows. I think this is likely related.


Solution:6

JDK7 will have builtin support for subprocess I/O redirection:

http://download.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html

In the meantime, if you really want to discard stdout/stderr, it seems best (on Linux) to invoke ProcessBuilder on a command that looks like:

["/bin/bash", "-c", "exec YOUR_COMMAND_HERE >/dev/null 2>&1"]  


Solution:7

In case you need to capture stdout and stderr and monitor the process then using Apache Commons Exec helped me a lot.


Solution:8

I believe the problem is the buffering pipe from Linux itself.

Try to use stdbuf with your executable

new ProcessBuilder().command("/usr/bin/stdbuf","-o0","*executable*","*arguments*");**  

The -o0 says not to buffer the output. The same goes to -i0 and -e0 if you want to unbuffer the input and error pipe.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »