Reputation: 330
I'm trying to write on a log file the output or a Java program launched via Runtime.getRuntime().exec(). I'm using the following code.
public class Thread_Write extends Thread {
int id;
public void run() {
try {
File log = new File("./log"+id+".txt");
log.createNewFile();
FileWriter fw = new FileWriter("./"+"log"+id+".txt",true);
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec("java WriteToFile "+id);
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
fw.write(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public Thread_Write(int i) {
super();
this.id = i;
}
public static void main(String[] args) {
for (int i =0 ; i<10;i++) {
(new Thread_Write(i)).start();
}
}
}
"java WriteToFile id" is suppose to write an exception on the terminal, but unfortunately, I'm not getting anything. What's odd is that if I change this command bye "echo test", "test" will be properly added at the end of the log file. Any idea why ?
Cheers.
Upvotes: 0
Views: 2111
Reputation: 328568
The likeliest reason is that the exception goes to the error stream. One option is to redirect the error stream too: process.getErrorStream()
.
Alternatively you can use a ProcessBuilder:
ProcessBuilder pb = new ProcessBuilder("java", "WriteToFile", String.valueOf(id));
pb.redirectErrorStream(true);
pb.redirectOutput(log);
Process p = pb.start();
Upvotes: 2
Reputation: 30723
There are several issues here. Here's how I would write the run() method (rationale follows):
public void run() {
try {
Process process = Runtime.getRuntime().exec("java WriteToFile " + id);
Thread err = consume(process.getErrorStream(),
new FileOutputStream("./" + "log" + id + ".txt"));
Thread std = consume(process.getInputStream(),
new FileOutputStream("./" + "log_stdout" + id + ".txt"));
err.join();
std.join();
} catch (Exception e) {
e.printStackTrace();
}
}
private Thread consume(final InputStream stream,
final OutputStream out) {
Thread result = new Thread() {
public void run() {
PrintWriter pw = new PrintWriter(out, true);
try (Scanner sc = new Scanner(stream)) {
while (sc.hasNext()) {
pw.println(sc.nextLine());
}
}
}
};
result.start();
return result;
}
(1) Most importantly: if WriteToFile
writes to both stderr and stdout, your process will block: as it only reads from stderr, there's no one to read the bytes off of the stdout buffer. Once this buffer fills up WriteToFile
will block on the next println(). Assuming this happens before an exception was written both processes will be deadlocked, each one waiting for the other one to make the first move. The solution: spawn two threads, one for consuming stdout and the other for consuming stderr.
Other (minor) issues:
(2) Instead of wrapping InputStream
in an InputStreamReader
which, in turn, is wrapped inside a BufferedReader
you can use the Scanner class: its constructor can take an InputStream and it gives you the readLine() method that you need.
(3) No need to create the log
variable if it sole purpose is to be used in log.createNewFile();
- the output file will be created for you by your FileWriter
object.
(4) You'd probably want to use a PrintWriter
object rather than a FileWriter
. The former gives you println()
which lets you print the output line-by-line.
Upvotes: 0
Reputation: 4165
Errors are not available through process.getInputStream()
, you need to instead use process.getErrorStream()
. An alternative is to first use processBuilder.redirectErrorStream(true)
. This will cause both stdout and stderr to be available on process.getInputStream()
.
Upvotes: 1