19

The Java I/O classes java.io.Reader, java.io.Writer, java.io.InputStream, java.io.OutpuStream and their various subclasses all have a close() method that can throw an IOException.

Is there any consensus on the proper way to handle such exceptions?

I have often seen recommendations to just silently ignore them, but that feels wrong, and at least in case of resources opened for writing, a problem while closing the file might mean that unflushed data could not be written/sent.

On the other hand, when reading resources, I'm totally unclear on why close() might throw and what to do about it.

So is there any standard recommendation?

A related question is Does close ever throw an IOException?, but that is more about which implementations really do throw, not about how to handle the exceptions.

Community
  • 1
  • 1
sleske
  • 81,358
  • 34
  • 189
  • 227
  • 1
    The question you link to ends with "If an exception is actually thrown, how should it be dealt with?". I suggest you put a bounty, possibly with an explaining comment, on that question if you really want an answer to it. – aioobe Jan 13 '11 at 11:16
  • @aioobe: Well, the linked question really contains two questions ("When do Stream/Reader/Writer actually throw on close?", and "How to handle the Exception"?). The first question has been answered (and the answer accepted), so I fear it would be confusing to post a bounty there. – sleske Jan 13 '11 at 11:19
  • Not if you add a comment. Besides, @erickson has a nice answer imo. – aioobe Jan 13 '11 at 11:22
  • 1
    @aioobe: Yes, but his answer is explicitly about streams which need to commit something, i.e. streams for writing, so it's not a complete answer to my question. – sleske Jan 13 '11 at 11:24
  • On a related note, the `close()` POSIX system call can return a status code indicating failure, and all I've ever done for that is to log it as a failure. – Raedwald Jan 13 '11 at 12:52
  • About the POSIX system call: Here's a post about checking the return value of `fclose()` (the Unix function to close a file), and the problems arising from not checking it: http://groups.google.com/group/comp.lang.c/msg/c601c3fd60dd536a?pli=1 – sleske Apr 14 '11 at 16:27

9 Answers9

7

Log it.

You can't really do anything about it (for example write some code that recovers from the error), but its generally worth letting somebody know about it.

Edit:
After further investigation and reading the other comments, I'd say that if you do want to handle it then you're going to have to know details of the implementation. Conversely you probably need to know details of the implementation to decide whether you need to handle it.

Realistically though, I can't think of any examples of streams where the reading or writing would work correctly without throwing an exception, but the closing would.

Qwerky
  • 18,217
  • 6
  • 44
  • 80
  • Depends on what the IOException is due to. If it is just due to some unflushed / pending data you could retry a few seconds later. (This does not apply for reading though.) – aioobe Jan 13 '11 at 11:21
  • @aioobe: I don't think pending data can cause an IOException. The Javadocs for `java.io.Writer.close()` explicitly say that `close()` will flush first. Do you have any reference for your claim? – sleske Jan 13 '11 at 11:31
  • +1 Agreed, there's not much you can do. Especially because you should be closing in a `finally` block *anyway*, and if even `close()` throws an exception there's a good chance that an exception is already being propagated from the `try` block. The only alternative situation I can think of, is if you knew you were using a particular `Writer` implementation that would do this in a known situation - and you were able to intelligently retry/work around the problem. With a general writer - just log/email/raise the error somehow and continue. – Andrzej Doyle Jan 13 '11 at 11:40
  • 1
    Good answer. BTW, there *are* cases where writing work correctly, but `close()` throws: If you `close()` while there are still unflushed changes, `close()` will flush, and that can throw. See the answer to _Does close ever throw an IOException?_ linked above. – sleske Jan 17 '11 at 00:14
  • It's quite possible that closing throws. Imagine a full disk with a tiny write to a file. All the data get buffered and flushing the buffer throws. But even if the probability is low, I wouldn't recommend exception swallowing. Simply re-throwing is usually best. – maaartinus Sep 16 '14 at 18:15
  • 1
    Log what, for what purpose? If your program can't describe or handle it, other than saying "something went wrong", how can someone reading the log file doing any better? If the log message does not result in any useful action, it might as well not be present. – Raedwald Sep 08 '18 at 10:24
4

"Ignoring or just logging is usually a bad idea." That's not answering the question. What should one do about an IOException from close()? Just rethrowing it only pushes the problem further up, where it's even more difficult to handle.

Theory

To answer you question directly: When this IO action failed, then depending on the circumstances, you may want to

  • rollback changes (don't leave a partially written file on the disk, delete it)
  • retry (maybe after a rollback)
  • ignore (and continue)
  • interrupt the current task (cancel)

You can often see the last 3 in dialog boxes shown to the user. Indeed, delegating choice to the user is a possibility.

I believe the main point is to not leave the system in an inconsistent state. Just swallowing a close exception might leave you with a crippled file, causing nasty errors later on.

Practice

It is a bit cumbersome to work with checked exceptions. Options arise:

  • Convert them to RuntimeException, throw them and leave them to be handled at a sufficiently high level

  • Keep using checked exceptions and suffer the syntactic pain

  • Use more composable error handling constructs, like the IO monad (not really available in Java, at least not without embarassingly many braces (see http://apocalisp.wordpress.com/2008/05/16/thrower-functor/) and not with full power)

More on IO monad

When you return a result in the IO monad context, you are not actually executing an IO action, but rather return the description of that action. Why? These actions have an API with which they compose nicely (versus the throws/try/catch massacre).

You can decide if you wish to do check exception style handling (with Option or Validation˙ in the return type), or dynamic style handling, with bracketing only on the finalunsafePerformIO` call (which actually executes the composed IO action).

To get some idea, see my Scala gist http://gist.github.com/2709535, which hosts the executeAndClose method. It returns both the result of the resource processing (if available) and an unclosed resource (if closing failed). It is then the users decision how to deal with such an unclosable resource.

It could be beefed up with Validation/ValidationNEL instead of Option if one needs the one/more actual exceptions too.

ron
  • 9,262
  • 4
  • 40
  • 73
  • The IO data type is available in Java here: https://github.com/functionaljava/functionaljava/blob/master/core/src/main/java/fj/data/IO.java – Apocalisp May 16 '12 at 12:53
1

Your code that calls InputStream.close() or OutputStream.close() is some high level code that delegates to the lower level InputStream or OutputStream class to perform some high level computation.

Whether to throw an exception

You have only 3 choices about what to do.

  • Catch and swallow the exception, discarding it.
  • Let it propagate upwards.
  • Catch the exception, and throw a different kind of exception. You would normally use a chained exception in this case.

The last two options are similar in that your code throws an exception. So this question is actually a special case of the question of when should my code throw an exception. The correct answer to that question in this context is: if, and only if, the alternative, is to fail to meet a post condition of your code or to maintain an invariant of your code. The post conditions specify what your method is meant to do. The invariants specify characteristics of the class.

So, you need to ask yourself whether the close() throwing an exception prevents your method doing what is should do.

  • If it does not prevent your method doing its job, the correct thing to do is to swallow the exception. Despite what many people will tell you.
  • If close() throwing does prevent your method doing its job, and the method may throw IOException, you can do nothing, just letting the exception propagate upwards.
  • If close() prevents your method doing its job, but the method may not throw IOException, you must catch the IOException and rethrow it as a different class of exception, recording the IOException as the cause of the thrown exception.

I know of no circumstances in which a InputStream.close() throwing an exception can prevent a computation succeeding. That call to close() naturally happens after you have finished reading whatever data you are interested in.

Output streams, however, can buffer data to be written to the output destination, either internally (within the Java code) or at a lower level (within the operating system). You therefore can not be sure that write operations to an output stream have actually resulted in real output until OutputStream.close() has successfully returned (without throwing an exception). You should therefore treat an exception thrown by OutputStream.close() just like a write failure.

Your method is responsible for maintaining its invariants. Sometimes this will require a clean-up or roll-back operation if close() throws an exception. You should put the code for that in a catch or finally clause for the IOException, even if you would like the exception to propagate upwards. If you use a catch clause and you want to propagate it, you will have to rethrow it.

Whether to log the exception

Some people will advise you to log the exception. This is almost always bad advice. Log messages are part of the user interface of your program. Fundamentally, you must always ask yourself whether to log anything at all, because useless verbiage can distract and confuse the users reading your log file ("users" includes system administrators). Every message logged should be for some useful purpose. Each should provide information that helps the user make decisions.

Reporting specifically that close() failed is rarely useful. How can it help a user make a decision? If the exception did not prevent your method from doing its job, there is no problem, and no actions by the user are necessary. If your program failed to close() the stream, and that is a problem, what can the user do to help?

Low level code is not usually responsible for logging at all. It instead performs abstract operations, reporting failure to higher level parts of your program by throwing exceptions. Code closing a stream is typically rather low level, so the code that detects that close() is too low level to do any logging.

The specific fact that close() failed is rarely useful. What can be useful is knowing that the abstract operation that your method is meant to perform failed. That can be done by having the higher level code catch all expected exceptions and reporting that the operation failed, rather than having your method precisely report that "close failed".

Raedwald
  • 46,613
  • 43
  • 151
  • 237
0

Try to use flush before you close the writer. The main reason for exception in close is that some other resources might have been using the data or the writer/reader may not be open at all. try to find wheather the resoures is open before closing it.

Aravind
  • 2,188
  • 4
  • 24
  • 28
  • Flushing before closing is superfluos: the javadoc of `java.io.Writer.close()` explicitly says: "Close the stream, flushing it first." – sleske Jan 13 '11 at 11:28
  • 1
    At any rate, this does not answer the question. The question is not why an exception might be thrown, but how to handle it, *especially* when I don't know why it was thrown. – sleske Jan 13 '11 at 11:29
  • Generally you want to flush buffering decorators but only the actual resource needs to be closed. You don't want to flush the decorator in the case of an exception. (Oh and there is a popular bug for an exception from `flush` in the `close` implementation to cause the `close` of the underlying resource to be skipped.) – Tom Hawtin - tackline Jan 13 '11 at 13:03
  • @Tom Hawtin: Why would I need to flush buffering decorators? I can just close the decorator, and it will take care of flushing & closing, for itself and any resources it uses. At least that is how I believe the JDK I/O classes work. – sleske Jan 13 '11 at 17:44
  • @sleske Generally you want to avoid flushing decorators in the exception case. Therefore you don't want to close the decorators in the exception case. You are going to close the underlying resource in the `finally` block, so there is no need to close the decorator. If you do close the decorator, the natural code will attempt to close the resource twice (although that should usually work, it's not nice). (There are issues if the decorator contains other resources.) – Tom Hawtin - tackline Jan 13 '11 at 17:50
  • @Tom: I don't understand what you mean by "decorator"? Are you talking about e.g. `BufferedInputStream`? – sleske Jan 13 '11 at 21:40
  • @sleske Any buffering decorator, whether it have `Buffered` in the name or just anything else that needs flushing. – Tom Hawtin - tackline Jan 13 '11 at 21:43
  • @Tom: Thanks. I still don't understand what you mean by "avoid flushing decorators" / "close the underlying resource". If I use e.g. a `BufferedInputStream`, I only have access to its methods. How should I "close the underlying resource", if not via BufferedInputStream.close()? I could try closing the original InputStream, of course, but that would probably violate the BufferedInputStream's contract, and for some decorators the underlying resource may not even be accessible. – sleske Jan 13 '11 at 21:48
  • @sleshe You had the underlying resource when you passed it to the decorator. If you are being strict, you should close the underlying resource even if the construction of the decorator failed. – Tom Hawtin - tackline Jan 13 '11 at 22:35
  • @Tom: Ah, I see (I hope :-)). You are basically saying "If I/O on the decorator throws, you don't want to flush it (since this will likely try to do more I/O, and throw again), but you *do* want to close the underlying resource to clean up e.g. native resources bound to it." Is that what you mean? – sleske Jan 14 '11 at 00:33
0

First of all don't forget to put the close() inside the "finally" section of your try catch block. Second you can surround the close method with another try catch exception and log the exception.

Reza
  • 1,478
  • 1
  • 15
  • 25
0

It depends on what you are closing. For example, closing a StringWriter does nothing. The javadocs are clear on that. In this case, you can ignore the IOException because you know that it will never be generated. Technically, you don't even need to call close.

/**
 * Closing a <tt>StringWriter</tt> has no effect. The methods in this
 * class can be called after the stream has been closed without generating
 * an <tt>IOException</tt>.
 */
public void close() throws IOException {
}

For other streams, log and handle the exception as appropriate.

dogbane
  • 266,786
  • 75
  • 396
  • 414
  • "log and handle the exception as appropriate." The "appropriate" part is just what my question is about :-). – sleske Jan 13 '11 at 11:42
  • 2
    So, the signature in the Java API is wrong. If `StringWriter.close()` really never throws an exception its signature ought to be `public void close()`, not `public void close() throws IOException`. – Raedwald Jan 13 '11 at 12:49
  • @Raedwald Not really... `StringWriter` is-a `Writer`, so must implement the `Closeable` interface. That's why it must have that signature. – dogbane Jan 13 '11 at 13:06
  • 1
    Is that really so? An overriding method can declare that it throws *fewer* exceptions than the method it overrides (I recall writing code that does this), so why not also with a method implementing part of an interface? This makes sense, because an object of the class can still be used in any context that knows only that it has an object that implemetns the interface. – Raedwald Jan 13 '11 at 13:17
  • true. Maybe they just generated the method names automatically. That's what I normally do - Eclipse:add unimplemented methods :) – dogbane Jan 13 '11 at 13:27
0

Generally resource handling should look like this:

final Resource resource = context.acquire();
try {
    use(resource);
} finally {
    resource.release();
}

In (the unlikely) case of release throwing an exception, any exception thrown by use will be dropped. I don't have a problem with the exception thrown closest to the catcher winning. I believe ARM blocks in JDK7(?) will do something crazy in this case, like rethrowing the use exception with the release exception attached.

If you use the Execute Around idiom, you can put these decisions and potentially messy code in one (or few) places.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
0

Ignoring or just logging is usually a bad idea. Even though it is true that you cannot recover from it, I/O exceptions should always be handled in a unified manner to make sure your software behaves in a unified way.

I would at least suggest handling like below:

//store the first exception caught
IOException ioe = null;
Closeable resource = null;
try{
  resource = initializeResource();
  //perform I/O on resource here
}catch(IOException e){
  ioe = e;
}finally{
  if (resource != null){
    try{
      resource.close();
    }catch(IOException e){
      if(null == ioe){
        //this is the first exception
        ioe = e;
      }else{
        //There was already another exception, just log this one.
        log("There was another IOException during close. Details: ", e);
      }
    }
  }
}

if(null != ioe){
  //re-throw the exception to the caller
  throw ioe;
}

The above is pretty verbose but works well, because it will prefer the IOException during I/O on the resource to the one during close, since it is more likely to contain information of interest to the developer. The code will also throw an IOException when something goes wrong, so you get a unified behaviour.

There might be nicer ways of doing it, but you get the idea.

Ideally, you would create a new exception type that would allow storing sibling exceptions, so in case you get two IOExceptions you could store them both.

rodion
  • 14,729
  • 3
  • 53
  • 55
  • 1
    "Ignoring or just logging is usually a bad idea." That's not answering the question. *What* should one do about an IOException from `close()`? Just rethrowing it only pushes the problem further up, where it's even more difficult to handle. – sleske Jan 13 '11 at 17:46
  • Why are you acquiring the resource within the `try`? That's just adding complication. Also note that non-`IOException` exceptions will suppress that `IOException`. – Tom Hawtin - tackline Jan 13 '11 at 17:57
  • @Tom: "Why are you acquiring the resource within the try?" Probably because the acquisition (e.g. `open()`in a file) may already throw. – sleske Jan 13 '11 at 21:39
  • @sleske: Sorry for not making it clear. `IOException` should be handled in the same way regardless of whether it is during the actual I/O or during `close()`. So the answer is: do the same handling as for other `IOException`s. If you are asking how to handle I/O exceptions in general, then that will depend on the kind of resource. Also, as others have pointed out, in certain cases you might wish to do different handling on `close()`, but that would be more of an exception than a rule. – rodion Jan 14 '11 at 03:10
  • @Tom: @sleske is right. In most API's I use, constructors throw an `IOException`. To be more specific, usually a `FileNotFoundException`. – rodion Jan 14 '11 at 03:13
  • @Tom: what exactly does not make sense? Resource acquisition may throw an `IOException`, that's a fact. As for the code, I do agree it is very hackish, but it conveys the idea. `IOException` on `close()` will shade any other `IOException` during resource manipulation, that's also a fact. There may be other ways of doing this, but I doubt they'll be any cleaner. As for non-`IOException`, it may shade the `IOException` but it's a general problem, not pertinent to my code. – rodion Jan 14 '11 at 12:38
-2

Well, in most cases, close() doesn't actually throw an IOException. Here's the code for InputStream.java:

  public void close() throws IOException
  {
    // Do nothing
  }

Errors from closing a network resource should really be of some type of RuntimeException, since you can disconnect a networked resource after the program connects to it.

You can see some example of various implementations of Reader/Writer and Streams using Google Code Search. Neither BufferedReader nor PipedReader actually throw an IOException, so I think you're mostly in the safe by not worrying about it. If you're really worried, you can check the implementation of the libraries you're using to see if you ever need to worry about the exception.

Like others mentioned, you can't do much about the IOException other than log it.

After all, try/catch blocks in the finally clause are pretty ugly.

Edit:

Further inspection reveals subclasses of IOException like InterruptedIOException, SyncFailedException, and ObjectStreamException, along with classes that inherit from it. So just catching an IOException would be too general -- you wouldn't know what to do with the information other than logging it because it could be from a whole range of errors.

Edit 2:

Urk, BufferedReader was a bad example, since it takes a Reader as input. I've changed it to InputStream.java

However, there's a hierarchy with InputStream <= FilterInputStream <= BufferedInputStream <= InputStreamReader (via inheritance and private instances) that all trickle up to the close() method in InputStream.

Ehtesh Choudhury
  • 7,452
  • 5
  • 42
  • 48
  • 1
    in your example, `close()` can throw an IOE because it calls `close()` on `in` which is a `BufferedInputStream`. – Qwerky Jan 13 '11 at 12:04
  • Actually, if it was `BufferedInputStream`, it would just trickle up to `InputStream`, which does nothing. `BufferedReader` instead takes a generic `Reader` as input. Therein lies the rub, because an implementation of Reader can have its own implementation of `close()` as well. – Ehtesh Choudhury Jan 13 '11 at 13:24
  • InputStream is an abstract class, the real closing logic would be implemented in a subclass. For example in FileInputStream the close method uses native code to actually close the file. – Jörn Horstmann Jan 13 '11 at 15:40