I have a Java program that connects to a local server via sockets and I would like to not have to start the server manually if Java detects that it isn't running. So if Java is not connected to the server then I use a ProcessBuilder to start it:
new ProcessBuilder(new String[]{"/bin/bash", "-c", "./start_server"}).start();
Which starts the server, and runs for about 4 minutes, until the server process doesn't respond. It's interesting that if I send more commands it takes less time for this to happen. I'm wondering if I am hitting a limitation of ProcessBuilder, possibly using too much memory. I print the communication to the server on the server side, since this is being called from ProcessBuilder I assume it's being directed elsewhere and herein lies the problem.
You HAVE to read the output (even if you just throw it away), both stdout and stderr. Otherwise the output buffer will fill up leading to odd behavior.
The simplest approach would be to redirect to the bit bucket (assuming Java 1.7+):
File bitbucket;
if (isWindows()) {
bitbucket = new File("NUL");
} else {
bitbucket = new File("/dev/null");
}
Process process = new ProcessBuilder("/bin/bash", "-c", "./start_server")
.redirectOutput(ProcessBuilder.Redirect.appendTo(bitbucket))
.redirectError(ProcessBuilder.Redirect.appendTo(bitbucket))
.start();
But if you have to use Java 1.6 or lower, you have to roll your own. This is what I use in my projects:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class StreamConsumer
{
private final InputStream stream;
private StreamConsumer(InputStream is)
{
this.stream = is;
}
private Runnable worker()
{
return new Worker();
}
private class Worker implements Runnable
{
@Override
public void run()
{
try (BufferedReader br = new BufferedReader(new InputStreamReader(stream))) {
while (br.readLine() != null) {
/* No one cares */
}
} catch (IOException ioe) {
/* No one cares */
}
}
}
public static void consume(InputStream stream, String label)
{
Thread t = new Thread(new StreamConsumer(stream).worker(), label);
t.setPriority(Thread.MIN_PRIORITY);
t.start();
}
}
And you would call it either like this:
Process process = new ProcessBuilder("/bin/bash", "-c", "./start_server")
.start();
StreamConsumer.consume(process.getInputStream(), "STDOUT");
StreamConsumer.consume(process.getErrorStream(), "STDERR");
Or like this:
Process process = new ProcessBuilder("/bin/bash", "-c", "./start_server")
.redirectErrorStream(true)
.start();
StreamConsumer.consume(process.getInputStream(), "STDOUT/STDERR");
Note that the ProcessBuilder
constructor takes a String...
(varargs), not a String[]
(although you can create and pass the String[]
manually if you want)