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

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?

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.

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");
}
}

So to avoid getting the error stream and input stream merged just remove the line
pb.redirectErrorStream(true);
Because as said in the java doc :
if this property is true, then any error output generated by subprocesses subsequently started by this object's start() method will be merged with the standard output, so that both can be read using the Process.getInputStream() method. This makes it easier to correlate error messages with the corresponding output. The initial value is false.
By calling pb.redirectErrorStream(true); you are merging both output.

getErrorStream states the following
Returns the input stream connected to the error output of the subprocess. The stream obtains data piped from the error output of the process represented by this Process object.
If the standard error of the subprocess has been redirected using ProcessBuilder.redirectError or ProcessBuilder.redirectErrorStream then this method will return a null input stream.
and the ReadLine states the following
Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed.
Based on the explanation provided to the API. readline waits indefinitely to get line feed or carriage return where as processbuilder returns NULL that is why readLine terminates for the same.

Related

Is there a way to achieve both reading process's OutputStream and redirecting it's output to standard io?

I failed to do both reading from process's OutputStream and redirecting it to standard io at the same time. I can do any one of the above, but not both.
I tried to use both inheritIO() and redirectOutput(ProcessBuilder.Redirect.PIPE) and it didn't work, I can read the output but it didn't appear in the standard output.
#Test
void testRedirectOutput() throws IOException, InterruptedException {
// when
Process proc = new ProcessBuilder()
.redirectErrorStream(true)
.inheritIO()
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.command("where", "where")
.start();
proc.waitFor();
// then
String output = readAllOutput(proc);
assertNotNull(output);
}
private static String readAllOutput(Process process) throws IOException {
StringBuilder builder = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
do {
line = reader.readLine();
builder.append(line);
}
while (line != null);
reader.close();
return builder.toString();
}
If I remove .redirectOutput(ProcessBuilder.Redirect.PIPE), it appears on the standard io but I can't read it (output=null).
Is there an elegant way to achieve it rather than calling System.out.println(output)?
Thanks.
A Process runs in parallel to your Java program. While it runs, it reads something from standard input and writes something to standard output and standard error. Typically, a process does not read all input at once and then write all output at once. Instead, it reads some input, does something with it, and then produces some output. This repeats itself until the process detects end of input, at which point it writes the remaining output to the standard output and closes it. In such cases, if you tried to write everything at once and then read everything at once, the OS buffers would fill up and your streams would become blocked in read/write operation.
This means you need a separate Thread to handle each stream. In addition to the thread that is running this code (probably main), you will need two other threads. If the input and output can fit into memory, you can simply pass the data around as ByteArrayInputStream and ByteArrayOutputStream to corresponding threads, and let them do the "pumping" of the data to/from the process.

BufferedReader not returning

I am using Java to run a program and reading the program's text output into a buffered reader. I then want to print out the results by doing this:
while((line=input.readLine()) != null){
System.out.println(line);
}
For some reason, when doing this the readLine() method does not return at all about halfway through the text, and the program waits indefinitely. Yet when running this program from the command line directly, its output is exactly as expected, just 100 lines or so of simple text. What could cause readLine() to not return halfway through reading simple text?
This may happen if nothing has been written to the stream.
You may often encounter the situation in which readLine() hangs is when you invoke a process that is supposed to write errors to stderr. When the process finishes you may try to read all the errors by doing:
BufferedReader brCleanUp = new BufferedReader(new InputStreamReader(stderr));
while ((line = brCleanUp.readLine()) != null) {
//process the error line
}
brCleanUp.close();
But if nothing at all was written to stderr, the above readLine() would hang as you described.
The way to avoid it is to check if there is any point of reading the stream (I don't know if it's a bug or not, but this worked for me:)
boolean available = stderr.available() >= 1;
if (available) {
BufferedReader brCleanUp = new BufferedReader(new InputStreamReader(stderr));
for( ; available && (line = brCleanUp.readLine()) != null;
available = stderr.available() >= 1) {
System.out.println ("[Stderr] " + line);
}
brCleanUp.close();
}
You need to encode the text document into UTF-8 like this, then use Scanner.
// specify file path
String filePath = "C:/path/path/file-location/file.txt";
// make a scanner with the UTF-8 param
Scanner scanner = new Scanner(new File(filePath),"UTF-8");
// loop through text
while (scanner.hasNextLine()){
System.out.println(scanner.nextLine());
}
There are actually a few ways to encode text like this, but the importance lies within the text encoding itself. Once you have the document in the correct format, the objects and classes you chose to read input are at your own discretion.
What could cause readLine() to not return halfway through reading simple text?
If you are reading from a file in a file system, this is possibly due to a problem with the file system; e.g. a remote file server is not responding.
If you are reading from a socket, either the remote server hasn't (yet) written / flushed the data you are trying to read, or a network problem is stopping the data from getting through.
If you are reading from a pipe (e.g. the output of another program), then the other program hasn't written / flushed the data you are trying to read.
(Another cases are reading from a "device file" or a file in the "/proc" file system, or a loopback file system. In these cases, the possible causes are difficult to enumerate.)

Process.getInputStream() doesnt return all lines

My code looks like this:
public List<String> linux(String... commands) throws Exception{
Process p = null;
ProcessBuilder builder = new ProcessBuilder(commands);
p = builder.start();
BufferedReader reader= new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8));
String line;
List<String> lines = new ArrayList<>();
while((line = reader.readLine()) != null) lines.add(line);
return lines;
}
But it didn't return all the outputs. I tried in the actual remote machine, there were about 100+ lines, but I only got 30 lines. Am I missing something ?
The code you have shown us will read all lines written by the process to its standard output. The readLine() call will only return a null when it has reached the end-of-stream for the pipe that it is reading from; i.e. after all reading all lines.
The readLine() call can only see the end-of-stream if the other end of the pipe (i.e. the external process) has closed it. You cannot get null by reading "too quickly". And conversely, if readLine() does return null for a given stream, any / all subsequent calls will return null. There is no point in retrying the readLine() if it returned null.
Also, you shouldn't use isAlive() to decide when to stop reading ... as suggested by someone else. The availability of unread output, and process termination are orthogonal issues.
Given that your code for reading the output is correct (I think so), there are just two possible explanations for the missing output:
Some of the process output was written to the processes standard error stream rather than its standard output.
Solution: You can get the contents of the standard error stream by reading from the stream returned by getErrorStream(). But a simpler alternative is to get ProcessBuilder to redirect the standard error output to standard output.
The external process terminated without flushing its output buffers. That could mean that some lines were never actually output by the process.
Solution: You need to fix the external application. This cannot be remedied from the Java application side.
It sounds like the Charset is not UTF-8 if it errors as soon as 'é' occurs. It's probably Extended ASCII, but if that doesn't work I'd try to figure out what Charset it really is.
It would fail on the entire line because of the BufferedReader.

Reading error stream from a process

I am writing a java program to read the error stream from a process . Below is the structure of my code --
ProcessBuilder probuilder = new ProcessBuilder( command );
Process process = probuilder.start();
InputStream error = process.getErrorStream();
InputStreamReader isrerror = new InputStreamReader(error);
BufferedReader bre = new BufferedReader(isrerror);
while ((linee = bre.readLine()) != null) {
System.out.println(linee);
}
The above code works fine if anything is actually written to the error stream of the invoked process. However, if anything is not written to the error stream, then the call to readLine actually hangs indefinitely. However, I want to make my code generic so that it works for all scenarios. How can I modify my code to achieve the same.
Regards,
Dev
readline() is a blocking call. It will block until there's a line to be read (terminated by an end of line character) or the underlying stream is closed (returning EOF).
You need to have logic that is checking BufferedReader.ready() or just using BufferedReader.read() and bailing out if you decide you're waiting long enough (or want to do something else then check again).
Edit to add: That being said, it shouldn't hang "indefinitely" as-is; it should return once the invoked process terminates. By any chance is your invoked process also outputting something to stdout? If that's the case ... you need to be reading from that as well or the buffer will fill and will block the external process which will prevent it from exiting which ... leads to your problem.
This is a late reply, but the issue hasn't really solved and it's on the first page for some searches. I had the same issue, and BufferedReader.ready() would still set up a situation where it would lock.
The following workaround will not work if you need to get a persistent stream. However, if you're just running a program and waiting for it to close, this should be fine.
The workaround I'm using is to call ProcessBuilder.redirectError(File). Then I'd read the file and use that to present the error stream to the user. It worked fine, didn't lock. I did call Process.destroyForcibly() after Process.waitFor() but this is likely unnecessary.
Some pseudocode below:
File thisFile = new File("somefile.ext");
ProcessBuilder pb = new ProcessBuilder(yourStringList);
pb.redirectError(thisFile);
Process p = pb.start();
p.waitFor();
p.destroyForcibly();
ArrayList fileContents = getFileContents(thisFile);
I hope this helps with at least some of your use cases.
Something like this might also work and avoid the blocking behaviour (without requiring to create a File)
InputStream error = process.getErrorStream();
// Read from InputStream
for (int k = 0; k < error.available(); ++k)
System.out.println("Error stream = " + error.read());
From the Javadoc of InputStream.available
Returns an estimate of the number of bytes that can be read (orskipped over) from this input stream without blocking by the nextinvocation of a method for this input stream. The next invocationmight be the same thread or another thread. A single read or skip of thismany bytes will not block, but may read or skip fewer bytes.
The simplest answer would be to simply redirect the error stream to stdout:
process.getErrorStream().transferTo(System.out);

Read output from external process

I am trying to run a .csh script and read it's output into a StringBuffer.
the output sometime returns empty although running the script from console returns some output. the same running flow can sometimes returns output and sometimes not, although nothing is changed in the way the process starts (same script, path , args) and the script isn't changed as well.
I'm not getting any exceptions thrown.
what might cause output now to be read correctly/successfully ?
the code segment is
public static String getOutpoutScript(Process p) {
InputStream outpout = p.getInputStream();
logger.info("Retrived script output stream");
BufferedReader buf = new BufferedReader(new InputStreamReader(outpout));
String line = "";
StringBuffer write = new StringBuffer();
try {
while ((line = buf.readLine()) != null) {
write.append(line);
}
} catch (IOException e) {
// do something
}
return write.toString().trim();
}
beside the fact not closing the streams is not good, could this or something else in the code might prevent output from being read correctly under some circumstances ?
thanks,
If you launch it with ProcessBuilder, you can combine the error stream into the output stream. This way if the program prints to stderr you'll capture this too. Alternatively you could just read both. Additionally, you may not want to use readLine, you could be stuck for awhile if the program does not print end of line character at the end.
Maybe you must replace p.getInputStream() with p.getOutputStream()
Besides this sometimes processes can block waiting on input, so you must read and write asynchronously - one possible solution is to use different threads - e.g. one thread is reading, other is writing and one that is monitoring the process.
If you have an error, this will write to getErrorStream() by default. If you have a problem, I would ensure you are reading this somewhere.
If the buffer for this stream fills, your program will stop, waiting for you to read it.
A simple way around these issues is to use ProcessBuilder.redirectErrorStream(true)

Categories