Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java io ugly try-finally block

Is there a not so ugly way of treat the close() exception to close both streams then:

    InputStream in = new FileInputStream(inputFileName);
    OutputStream out = new FileOutputStream(outputFileName);

    try {
        copy(in, out);
    } finally {
        try {
            in.close();
        } catch (Exception e) {
            try {
                // event if in.close fails, need to close the out
                out.close();
            } catch (Exception e2) {}
                throw e; // and throw the 'in' exception
            }
        }
        out.close();
    }

update: All the above code is within one more try-catch, thanks for the warnings.

FINALLY (after the answers):

And a good utility method can be done using Execute Around idiom (thanks Tom Hawtin).

like image 573
The Student Avatar asked Apr 23 '10 14:04

The Student


3 Answers

This is the correct idom (and it works fine):

   InputStream in = null;
   OutputStream out = null;
   try {
       in = new FileInputStream(inputFileName);
       out = new FileOutputStream(outputFileName);
       copy(in, out);
   finally {
       close(in);
       close(out);
   }

  public static void close(Closeable c) {
     if (c == null) return; 
     try {
         c.close();
     } catch (IOException e) {
         //log the exception
     }
  }

The reason this works fine is that the exception thrown before you got to finally will be thrown after your finally code finishes, provided that your finally code doesn't itself throw an exception or otherwise terminate abnormally.

Edit: As of Java 7 (and Android SDK 19 - KitKat) there is now a Try with resources syntax to make this cleaner. How to deal with that is addressed in this question.

like image 141
Yishai Avatar answered Nov 02 '22 06:11

Yishai


You could implement a utility method:

public final class IOUtil {
  private IOUtil() {}

  public static void closeQuietly(Closeable... closeables) {
    for (Closeable c : closeables) {
        if (c != null) try {
          c.close();
        } catch(Exception ex) {}
    }
  }
}

Then your code would be reduced to:

try {
  copy(in, out);
} finally {
  IOUtil.closeQuietly(in, out);
}

Additional

I imagine there'll be a method like this in a 3rd party open-source library. However, my preference is to avoid unnecessary library dependencies unless I'm using a large portion of its functionality. Hence I tend to implement simple utility methods like this myself.

like image 32
Adamski Avatar answered Nov 02 '22 08:11

Adamski


try {
    final InputStream in = new FileInputStream(inputFileName);
    try {
        final OutputStream out = new FileOutputStream(outputFileName);    
        try {
            copy(in, out);
            out.flush(); // Doesn't actually do anything in this specific case.
        } finally {
            out.close();
        }
    } finally {
        in.close();
    }
} catch (IOException exc) {
    throw new SomeRelevantException(exc);
}

Remember that opening a stream may throw an exception, so you do need a try between the stream openings (please don't do some hack involving nulls. Anything can throw an Error (which are not an instances of Exception).

It turns out that catch and finally should rarely share the same try.

Since Java SE 7 you can write use try-with-resource to avoid so much indentation. It more or less does the same thing although there are suppressed exception hidden away.

try (
    final InputStream in = new FileInputStream(inputFileName);
    final OutputStream out = new FileOutputStream(outputFileName);    
) {
    copy(in, out);
    out.flush(); // Doesn't actually do anything in this specific case.
} catch (IOException exc) {
    throw new SomeRelevantException(exc);
}

You may want to use the Execute Around idiom.

I believe the standard good way to copy is using NIO's transferTo/transferFrom.

like image 19
Tom Hawtin - tackline Avatar answered Nov 02 '22 06:11

Tom Hawtin - tackline