gamlieldor gamlieldor - 4 years ago 144
Java Question

ProcessBuilder BufferedReader read() blocking

I'm using

ProcessBuilder
to run a command line tool we are using. During its run the tool asks 2 yes/no questions, so usually I answer 'y' twice and then press enter after each time. My problem is that the tool always finishes its run when running from cmd, but when I run it through my java code, it sometimes work and sometimes gets stuck on
while ((n = op.read(buffer)) != -1)
(with the same input).

Here is my code. Am I doing something wrong? What am I missing? Thanks.

List<String> processArgs = new ArrayList<>();
processArgs.add(0, "java");
processArgs.add(1, "-jar");
processArgs.add(2, JAR_PATH);
processArgs.add(3, "-put");
processArgs.addAll(args);
try
{
// run tool with put
ProcessBuilder pb = new ProcessBuilder(processArgs);
pb.directory(new File("src\\temp"));
pb.redirectErrorStream(true);
Process p = pb.start();

// write 'y' to the tool's stdin.
String answer = "y" + System.getProperty("line.separator");
// yes to first question
p.getOutputStream().write(answer.getBytes());
p.getOutputStream().flush();

// read tool's process stdout
this.op = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringWriter sw = new StringWriter();
int n = 0;
boolean answered = false;
char[] buffer = new char[BUFFER_SIZE];
while ((n = op.read(buffer)) != -1)
{
sw.write(buffer, 0, n);
if (sw.toString().contains("second question") && !answered)
{
// yes to second question
p.getOutputStream().write(answer.getBytes());
p.getOutputStream().flush();
answered = true;
}
}

stdout = sw.toString();
exitCode = p.waitFor();
}
catch (IOException | InterruptedException e)
{
throw new ToolException("process had an exception:\n" + e.getMessage());
}


UPDATE:
I changed my code and added
pb.redirectErrorStream(true)
, but now the process still gets blocked on
op.read(buffer)
. When I debug it seems that it's stuck on the second question, even though I wrote 'y' twice to the output stream. Am I using
getOutputStream()
incorrectly?

SECOND UPDATE:
The second question didn't get the second 'y' as an answer, and it caused the process to wait for input. I changed the code so i will show the proper way to insert input to the subprocess outputstream.

Answer Source

The output and error stream are buffered. When the buffer fills up the program stops waiting for you to read it. However you only read the output first so if the error stream fills you have a deadlock.

A simple solution is to redirect the error to the output so you have only one stream to read. i.e.

pb.redirectErrorStream(true);

As per the documentation https://docs.oracle.com/javase/8/docs/api/java/lang/ProcessBuilder.html

Alternatives include; writing error to a file, or reading it in another thread.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download