Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BufferedReader.readLine blocks my program but BufferedReader.read() reads properly

I have a snippet as follows:

Process proc = Runtime.getRuntime().exec(command);
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
String line = br.readLine();

Now in the above code I am sure that process will always have on line input, so I did not use any kind of while loop or any null check. The problem is readLine blocks. The one reason I was aware of is, the stream having no data to read and hence readLine keeps waiting. To check this, I removed readLine and used read() function as follows:

Process proc = Runtime.getRuntime().exec( command );
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
int a;
while((a=br.read())!=-1){
    char ch = (char) a;
    if(ch == '\n')
        System.out.print("New line "+ch);
    if(ch == '\r')
        System.out.print("Carriage return "+ch);
    System.out.print(ch);
}

To my surprise this code worked and printed the messags New line and Carriage return. Now I am wondering why did the readLine block? The data is available it is terminated by newline. What else could be the reason??

Note: The above worked once in a while! Maybe once out of 15times.
Note: I tried using ProcessBuilder too, but same behaviour.

UPDATE: So I switched to ProcessBuilder and then I redirected the errorStream and now I get both input stream and error stream at once when I do process.getInputStream and this works fine. Below is the snippet.

ProcessBuilder pb = new ProcessBuilder(command.split(" "));
pb..redirectErrorStream(true);
Process proc = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = br.readLine();
//Now I get both input and error stream.

I would like to differentiate my error stream from input stream but with this method they are all jumbled up! Any ideas on this?

like image 233
Destructor Avatar asked Dec 16 '14 14:12

Destructor


2 Answers

You can use threads, in order to avoid it.

Like One slave thread which will be responsible for reading. This will not halt your progress of program.

like image 170
Arvind Bishnoi Avatar answered Sep 24 '22 04:09

Arvind Bishnoi


I think the problem is not that the standard error is blocking, but that the standard output is blocking causing the application you are calling to block.

Standard output is normally buffered. If the process you are calling writes less to standard output than the buffer size all is well and it can reach the code that writes to standard error. If the process fills the buffer, its attempt to write to standard output will block and it will never reach the point where it writes to standard error.

This could be why you see it working occasionally - sometimes the standard output does not fill the buffer. It could also be why it works after a long time: eventually the write to standard output times out.

As a demonstration, this simple process always block like you describe on my Windows 8 machine:

public class Proc {

    public static void main(String[] args) {
        for(int i=0;i<1000;i++) {
            System.out.print("More data ");
        }
        System.out.println();
        System.err.println("An error line");
    }
}
like image 45
Simon G. Avatar answered Sep 23 '22 04:09

Simon G.