In Java, can a process pipe stream reader encountering an IOException recover? - java

I have some Java code, which starts other processes and reads their output using a BufferedReader, by calling java.io.BufferedReader#readLine() in a loop. Sometimes I see that java.io.BufferedReader#readLine() throws IOException, e.g. when the process quits unexpectedly (such things can happen in my case) etc.
The question is: what is the appropriate handling of an BufferedReader#readLine() throwing IOException: is there any case, when reading from the stream again could continue consuming the process output (assuming it was some kind of transient error), or does IOException mean the stream is in failure and should be abandoned and closed?
I guess the whole issue boils down to one thing, if a a pipe stream encountering an I/O error can later recover from it and continue reading or not. My gut instinct tells me, it depends on the underlying operating system / JVM implementation, but I would love to hear inputs from people better versed in this topic than myself.

Related

what's the difference between ClosedChannelException and -1 in Java SocketChannel read function

I've seen the java doc, and it says that :
The number of bytes read, possibly zero, or -1 if the channel has reached end-of-stream
I wonder whether the '-1' means that the connection is closed?
If it is, then why there is an exception named ClosedChannelException it throws?
What's the difference between these two concepts?
Exceptions are usually used in situations where the usual flow of an application cannot continue and some special handling needs to be applied. Especially exceptions should never be used for control flow handling for events that are expected/usual.
In your situation the JavaDoc clearly states, that -1 is returned if the end of the stream has been reached. For example you read an image file over a stream and all bytes of the image have been read, then -1 is returned to notify your code that no more data is to be expected. This is a usual situation and part of the normal control flow. On the other hand the ClosedChannelException is thrown, if the channel was locally (i.e. by you) closed and you continue reading. This is unexpected. The data was not fully read and the application cannot continue as usual, as there is - in this example - no image to display.
Another reason - apart from mixing expected and unexpected program flows - for not using Exceptions for control flow in expected situations is performance. In Java Exceptions are a costly thing. An Exception is a pretty large object and collecting the current stacktrace takes some considerable amount of time.
UPDATE 2022-02-14:
Reading data from a remotely closed channel, after the receiving the available data, simply results in 0 bytes read and not in a ClosedChannelException. I adapted the answer accordingly. Thanks #user207421 for bringing this up.

Input/Output Stream : End of Stream?

I was always wondering: What is the end of a stream?
In the javadoc of most readLine methods in the java.io package, you can read that "this returns null if the end of the stream is reached" - though I never actually got a null, as most streams (in the case of a network stream that I use most often) just block the program execution until something is written into the stream on the remote end
Are there ways to enforce this acutal behavior happening in an actual non-exception throwing way? I am simply curious ...
Think of a file being read. There is an end of stream there, the end of the file. If you try to read beyond that, you simply can't. If you have a network connection though, there doesn't need to be an end of stream if you simply wait for more data to be sent.
In the case of the file, we know for a fact that there is no more data to be read. In the case of a network stream we (usually) don't.
Blocking a FileReader when no more data is available, awakening when there is: the simple answer is: you can't. The fundamental difference is that you read a file actively, but when you listen to a network stream you read passively. When something comes from the network your hardware sends a short of signal to the Operating System, which then gives the new data to your JVM, and the JVM then awakens your process to read the new data (so to speak). But we don't have that with a file, at least not immediately.
A possible workaround would be to make a wrapper to the StreamReader you have, with a listener that is notified when the file is changed, which then awakens you to read further. In Java 7 you can use the WatchService.
At some point, the socket will be closed, and no more data can be sent via that stream. This is when the InputStream will signal EOF by returning -1 from read() and its overloads. This state is irreversible. That stream is dead.
Simply blocking for more data on an open stream is not an EOF condition.
I never actually got a null, as most streams (in the case of a network stream that I use most often) just block the program execution until something is written into the stream on the remote end
No. You never got a null because the peer never closed the connection. That's what 'end of stream' means. It doesn't mean 'no more data for the time being'.

Why is InputStream.close() declared to throw IOException?

The java.io.InputStream.close() method is declared to throw an IOException. Under what circumstances would such an exception actually be thrown?
Edit: Yes I have read the javadoc. Can anybody be more specific than "when an I/O error occurs"? What I/O error can occur while closing an InputStream?
In the case of input stream reading from a file system, an error can be raised while the file system itself updates the last access time metadata, or some other metadata, on a file during closure. Anyway, this happens almost never in practice.
In the case of an input stream reading from a network connection, an error on closure is a bit more conceivable. A normal closure of a network socket actually involves a closure request (TCP/IP FIN packet) being sent over the connection and waiting for the other end to acknowledge this closure request. (In fact, the other end of the connection then in turn sends a closure request, which the closing end acknowledges.) So in the case of a socket input stream, a closure operation actually involves sending traffic over the connection and the closure can thus fail with an error.
Note that in many implementations, close() generally doesn't throw an IOException if the stream is already closed; it simply fails silently to close the stream again.
I'm looking through the Java source code, and have found something interesting which is indicative of the IOException reason. InputStream is an abstract class. We therefore can't predict the kind of input which will be closed, so it's good to keep information flowing.
Whatever code uses that input stream needs to be able to throw an IOException, because there is a chance that closing the input stream may fail. If it fails, whatever's using the implementation needs to know about it, because there's a good chance it needs to be handled.
It's important to understand the layout of the Exception structure in Java. Every exception, of course, extends Exception. However, there are also broader categories of exceptions: java.lang.IOException is one of these, and covers all possible input/output exceptions. When we say there has been an I/O error, we're referencing anything which falls under IOException. As a result, many exceptions extends this one, e.g. FileNotFoundException, EOFException, etc. as it's important to have a broad, overarching exception to manage these.
As a result, any IO class will need to be able to throw any of the various IOExceptions on close. close() therefore must throw IOException - this gives its implementation the ability to throw any of the extensions of IOException as well. This is why close() throws IOException - exceptions are inherited, and you need to be able to any of the IOExceptions when closing a stream.
Here are a couple scenarios worth noting:
You can't close an IOStream twice, though this generally doesn't throw an exception if you do
The content becomes inaccessible (e.g. a disk was unmounted) (The close() is actually critical for the operating system, as it needs to have an indicator of when the file is no longer busy)
The generic source has closed
Any generic failure not covered by all other subclasses of IOException (e.g. FileNotFoundException)
You can check what caused an IOException by running Exception.getMessage().
The underlying close system call will have to be made eventually, e.g. on linux http://linux.die.net/man/2/close. That call is documented to fail with EIO: "An I/O error occurred." So the reason is, that the underlying file system close call can fail.
I have wondered about this myself and have done a little research on this topic few years ago. This is what I know....
If you look at the javadoc in the link you provided, it clearly states that "The close method of InputStream does nothing", so there is no way it would throw an exception, correct? But then looking at all of the subclasses of IOException you will see that there are many situations where subclasses of inputstream may not be able to close the stream. So my best guess is that this exception is designed for subclasses to make use of it.
http://docs.oracle.com/javase/6/docs/api/java/io/IOException.html
In some cases, it is nothing more than a nuisance, and in others it clearly indicates that something went wrong. It all depends on what type of inputstream implementation you are making use of.

How to make readInt() block on input byte stream?

I have an input stream coming form a blackbox (say B). All the messages coming in from this stream are serialized binary data and each message starts with a four byte int. Most of it is logging data and runs 24 hrs a day. I read these four bytes using readInt() method. Now, ocasionally, the main thread would exit with EOFException and crash the program.
After researching on this, I found that it happens when there are less than four bytes in the input stream at the time of readInt(). My guess is that the buffer is not filling in fast enough between successive reads. Some of the possible solutions I am thinking of include checking available() before reading (consumes too many cycles considering the amt of data) or restart when exception occurs (sounds like poor programing). If only I could block using readInt(), it would be the best way, I think. I've looked at implementation of readInt() but again it boils down to blocking with read().
Anyone knows of a better solution?
Any blocking call down the call hierarchy is "bound" to make all the calls up the chain blocking as along as both calls are part of same thread of execution. The readInt method of DataInputStream makes four calls to the read method of the underlying input stream which will surely block as long as the data is not made available hence your fear of "buffer doesn't fill in fast enough" doesn't seem to be logical.
I have encountered these kind of exceptions in cases where either the server process dies or or drops the connection in which case the client ends up reading a -1 and throws an exception. Are you gobbling any sort of exceptions in your client/server code? Do your logs show anything suspicious?
I believe that you are using DataInputStream. That class throws EOFException in situation when the stream which it wraps, returns -1 from the read() method (which actually blocks until input data is available).
I suppose, you should have a look why the main stream's read returns with -1 in the first place.
The basic InputStream interface requires blocking reads, the EOFException you get is thrown when the readInt() encounters the end of stream marker, since returning the incomplete int would be a bad idea it throws the End Of File Exception.
The EOFException is thrown because the other end of the stream reached its end,has been closed or is no longer connected. You should check if the blackbox terminates the connection.
Since the stream is network based your socket may have a timeout set, if this is the case try changing the SOTimeout value of the socket.

when does java.util.zip.ZipFile.close() throw IOException?

Under what circumstances would java.util.zip.ZipFile.close() throw an IOException? Its method signature indicates that it can be thrown, but from the source code there doesn't seem to be any place where this could happen, unless it's in native code. What corrective action, if any, could be taken at the point where that exception is caught?
From the API docs on ZipFile.close():
Closing this ZIP file will close all of the input streams previously returned by invocations of the getInputStream method.
And InputStream.close() throws an IOException, so ZipFile.close() has to throw it too. According to the API docs for InputStream.close(), it throws an IOException "if an I/O error occurs". That's not very descriptive but it's casting a wide net. InputStreams can represent streams coming from the filesystem, network, memory, etc. InputStreams can involve buffers that need to be flushed, sockets that need to be closed, resources that need to be freed, locks that need to be freed, etc. IOExceptions can happen for a variety of reasons.
From man close(2):
Not checking the return value of close() is a common but nevertheless serious programming error. It is quite possible that errors on a previous write(2) operation are first reported at the final close(). Not checking the return value when closing the file may lead to silent loss of data. This can especially be observed with NFS and with disk quota.
I'm not sure but I think IOException is thrown when one of the following events happen:
The zip file was deleted by something/someone outside of the application.
When the drive that contains the zip file is unmounted/disconnected
A lot more events might be the reason but those are the only two I could think of right now.
The documentation for ZipFile.close() says:
Closing this ZIP file will close all of the input streams previously returned by invocations of the getInputStream method.
Presumably the native close method is performing the close the InputStreams.
The close method of InputStream has IOException as a checked exception.
The most likely cause is an out of space condition on the filesystem where the zip file is being written error in the underlying filesystem. Unless you can identify the cause and work around it on the fly, all you can do is report the condition to the user.

Categories