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.
-
4Probably because it's written to the error stream of the process, not its input stream.assylias– assylias2014年01月16日 10:04:17 +00:00Commented Jan 16, 2014 at 10:04
3 Answers 3
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();
1 Comment
redirectOutput(log) instead.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().
Comments
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.