This is a follow up to this question. The answer suggested there is
to copy the Process out, err, and input streams to the System versions
You should instead use the ProcessBuilder.redirectOutput
method & friends. Read more here
The problem is that IOUtil.copy()
is running while there is data in the InputStream to be copied. Since your process only produces data from time to time, IOUtil.copy()
exits as it thinks there is no data to be copied.
Just copy data by hand and use a boolean to stop the thread form outside:
byte[] buf = new byte[1024];
int len;
while (threadRunning) { // threadRunning is a boolean set outside of your thread
if((len = input.read(buf)) > 0){
output.write(buf, 0, len);
}
}
This reads in chunks as many bytes as there are available on inputStream and copies all of them to output. Internally InputStream puts thread so wait()
and then wakes it when data is available.
So it's as efficient as you can have it in this situation.
Process.getOutputStream()
returns a BufferedOutputStream
, so if you want your input to actually get to the subprocess you have to call flush()
after every write()
.
You can also rewrite your example to do everything on one thread (although it uses polling to read both System.in and the process' stdout at the same time):
import java.io.*;
public class TestProcessIO {
public static boolean isAlive(Process p) {
try {
p.exitValue();
return false;
}
catch (IllegalThreadStateException e) {
return true;
}
}
public static void main(String[] args) throws IOException {
ProcessBuilder builder = new ProcessBuilder("bash", "-i");
builder.redirectErrorStream(true); // so we can ignore the error stream
Process process = builder.start();
InputStream out = process.getInputStream();
OutputStream in = process.getOutputStream();
byte[] buffer = new byte[4000];
while (isAlive(process)) {
int no = out.available();
if (no > 0) {
int n = out.read(buffer, 0, Math.min(no, buffer.length));
System.out.println(new String(buffer, 0, n));
}
int ni = System.in.available();
if (ni > 0) {
int n = System.in.read(buffer, 0, Math.min(ni, buffer.length));
in.write(buffer, 0, n);
in.flush();
}
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
}
}
System.out.println(process.exitValue());
}
}