29

After providing some answers here, and reading some comments, it would seem that, in practice IOException is never thrown on close for file I/O.

Are there any cases in which calling close on a Stream/Reader/Writer actually throws an IOException?

If an exception is actually thrown, how should it be dealt with?

Danubian Sailor
  • 1
  • 38
  • 145
  • 223
TofuBeer
  • 60,850
  • 18
  • 118
  • 163

5 Answers5

29

I have found two cases:

  • Losing the network connection when there is still data in the buffer to be flushed.
  • Having the file system fill up (or reaching your user limit for file size) when there is still data in the buffer to be flushed.

Both of those examples depend on something happening while there is still data in the buffer. Close flushes the buffer before the file is closes, so if there is an error writing the data to the file it throws an IOException.

If you execute the following code passing it the name of a file to create on a network drive, and then before you press the enter key unplug your network cable, it will cause the program to throw an IOException in close.

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class Test
{
    public static void main(final String[] argv)
    {
        final File file;

        file = new File(argv[0]);
        process(file);
    }

    private static void process(final File file)
    {
        Writer writer;

        writer = null;

        try
        {
            writer = new FileWriter(file);
            writer.write('a');
        }
        catch(final IOException ex)
        {
            System.err.println("error opening file: " + file.getAbsolutePath());
        }
        finally
        {
            if(writer != null)
            {
                try
                {
                    try
                    {
                        System.out.println("Please press enter");
                        System.in.read();
                    }
                    catch(IOException ex)
                    {
                        System.err.println("error reading from the keyboard");
                    }

                    writer.close();
                }
                catch(final IOException ex)
                {
                    System.err.println("See it can be thrown!");
                }
            }
        }
    }
}

Since Java 7 you can use try-with-resources to get out of this mess (removed explicit exception generation code for the close() operation):

private static void process(final File file) {
    try (final Writer writer = new FileWriter(file)) {
        writer.write('a');
    } catch (final IOException e) {
        // handle exception
    }
}

this will auto-magically handle the exceptions in close() and it performs an explicit null check internally.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
TofuBeer
  • 60,850
  • 18
  • 118
  • 163
17

When it does happen, it should be handled like any other IOException, not silently ignored like you see recommended so often. The assumption is, I guess, that since you're done using the stream, it doesn't matter if it was cleaned up properly.

However, cleaning up properly is important. If a close() operation does raise an exception, its likely that it involved flushing some output, committing some transaction (in the case of a database connection you thought was read-only), etc.—definitely not something that should be ignored. And, since it is rare, you're not compromising the reliability of your application significantly by aborting the operation.

erickson
  • 265,237
  • 58
  • 395
  • 493
  • 3
    Yes, but what if you only opened the file for reading? Then the problem of flushing or commmitting does not apply. – sleske Jan 13 '11 at 10:57
  • 2
    Some form of "commit" may apply, for example the release of locks. I agree that it would be very surprising to see an exception raised when closing any sort of "read-only" resource... this would be such a rare event that I would definitely want to log it. Ignoring it with an empty catch block would not improve the quality of the program. – erickson Jan 13 '11 at 21:09
15

For files, you may not see IOException thrown often on close(), but you'll definitely see it for non-File I/O like closing sockets to the network.

Here's an example of a Java bug where closing a UDP socket eventually caused an IOException to be thrown.

Martin
  • 2,815
  • 1
  • 21
  • 30
  • yup, anything that can "go away" can cause that to hapen. A hard drive crash could probably do it too. – TofuBeer Feb 26 '09 at 00:25
5

It's specifically FileInputStream.close which does not throw, even if your hard drive is on fire. Presumably it is the same for socket input. For output streams you may also be flushing. Until relatively recently [see timestamps] BufferedOutputStream used to fail to close the underlying stream if flush threw.

(@MaartenBodewes would like me to point out that FileInputStream.close not throwing is not specified by the API docs. At the time of the post it was customary to elide the clause mentioning that this related to the Sun JDK (now known as Oracle JDK and OpenJDK). It appears that an obscure former reimplementation called Apache Harmony which Android used to use may have had different behaviour. Potentially other implementations, or versions of OpenJDK, may also throw.)

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

An examination of what can happen when calling close, how exception hiding can affect you and what you can do about it: blog post.

McDowell
  • 107,573
  • 31
  • 204
  • 267