While making some code updates the other day, I discovered a couple of potentially confusing features of the java.io
package that I wasn't previously aware of:
- The
PrintStream
andPrintWriter
classes consume exceptions. From thePrintWriter
documentation:
Methods in this class never throw I/O exceptions, although some of its constructors may. The client may inquire as to whether any errors have occurred by invoking checkError().
Because I had incorrectly assumed that a
PrintWriter
would propagate any exceptions thrown by the underlying stream, I hadn't been callingcheckError()
. The result was that my servlet class, which was using the print writer returned byServletResponse#getWriter()
, failed to detect when a connection had been terminated by the client. The servlet simply continued writing to the output stream. Once I started calling checkError(), the response was correctly terminated:if (writer.checkError()) { throw new IOException("Error writing to output stream."); }
I couldn't find any explanation as to why these two classes were written this way, while all of the other classes in
java.io
appear to simply propagate exceptions. - The
read(byte[], int, int)
method of theInputStream
class also consumes exceptions. From the Javadoc:
The
read(b, off, len)
method for classInputStream
simply calls the methodread()
repeatedly. If the first such call results in anIOException
, that exception is returned from the call to theread(b, off, len)
method. If any subsequent call toread()
results in aIOException
, the exception is caught and treated as if it were end of file.This is extremely misleading, since it completely obscures the fact that an error occurred and makes it appear as though the stream terminated normally.
So, even though this behavior is not what I had expected, at least it is documented, and is something I'll now be aware of when using these classes in the future.