views:

195

answers:

4

Hello, everyone!

Findbugs bugs me about a method which opens two Closeable instances, but I can't understand why.

Source

public static void sourceXmlToBeautifiedXml(File input, File output)
        throws TransformerException, IOException, JAXBException {

    FileReader fileReader = new FileReader(input);
    FileWriter fileWriter = new FileWriter(output);

    try {
        // may throw something
        sourceXmlToBeautifiedXml(fileReader, fileWriter);
    } finally {
        try {
            fileReader.close();
        } finally {
            fileWriter.close();
        }
    }
}

Findbugs analysis

Findbugs tells me

Method [...] may fail to clean up java.io.Reader [...]

and points to the line with FileReader fileReader = ...

Question

Who is wrong: me or Findbugs?

A: 

I think findbugs is right.

} finally {
    try {
        fileReader.close();
    } finally {
        fileWriter.close();
    }
}

In this block you try to close your FileReader. This however can throw an exception and in the nested finally you close the fileWriter. Have you tried closing both readers in the same finally block? What does findbugs say then?

} finally {
    try {
        fileReader.close();
        fileWriter.close();
    } finally {
        //dunno maybe log that something went wrong.
    }
}
er4z0r
if the fileReader.close() throws an IOException, fileWriter will never be closed.
duffymo
Your critic applies to your second code example. -- Yes `fileReader.close()` may throw `IOExceptioon`, that's why in my example `fileWriter.close` is in a finally block: if closing fileReader fails, fileWriter gets closed anyway.
java.is.for.desktop
Darn. Got me. Rule of thumb: never go to stackoverflow before your first coffee ;-)
er4z0r
+1  A: 

i'd say it's you.

i'd close both resources in a separate try/catch block. i'd create static methods to help me:

public static void sourceXmlToBeautifiedXml(File input, File output)
        throws TransformerException, IOException, JAXBException {

    FileReader fileReader = new FileReader(input);
    FileWriter fileWriter = new FileWriter(output);

    try {
        // may throw something
        sourceXmlToBeautifiedXml(fileReader, fileWriter);
    } finally {
        close(fileReader);
        close(fileWriter);
    }
}


// same for reader & writer
public static void close(InputStream s)
{
    try
    { 
       if (s != null)
       {
           s.close();
       }
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
}
duffymo
Hmm, I can't believe that this is a common pattern when closing multiple resources. Is there some pattern for this?
java.is.for.desktop
There sure is for JDBC. It's common for ResultSet, Statement, and Connection.
duffymo
I don't know if it's a "pattern", but that static method is the way I write it.
duffymo
+3  A: 

This may be complicated even for findbugs.

try {
   fileReader.close();
} finally {
   fileWriter.close();
}

Seems to me you are right.

EDIT : Wow, I thought I will get voted down for saying findbugs can be wrong!

EDIT : Looks like FindBugs is right after all. Good catch meriton.

fastcodejava
Well, if you had said that findbugs *is* wrong I'd probably have downvoted, but you are certainly correct in saying that it *can* be.
meriton
+4  A: 

FindBugs is correct: If the FileWriter's constructor throws an exception, the file reader will not be closed. To verify this, try passing an invalid filename for output.

I'd do it as follows:

    FileReader fileReader = new FileReader(input);

    try {
        FileWriter fileWriter = new FileWriter(output);
        try {
            // may throw something
            sourceXmlToBeautifiedXml(fileReader, fileWriter);
        } finally {
            fileWriter.close();
        }
    } finally {
        fileReader.close();
    }

Note that the handling of exception thrown when closing could be improved, since leaving a finally-block by throwing an exception will cause the try-statement to terminate by throwing that exception, swallowing any exception thrown in the try-block, which generally would be more useful for debugging. See duffymo's answer for a simple way on how to avoid this.

meriton
Good catch! FindBugs rocks.
fastcodejava
+1 for being the only person to note that the *constructor* can throw. Personally, I'd call the ctors inside the `try` (explicitly initializing the variables to `null` outside it), then use Jakarta `IOUtils.closeQuietly()` inside the finally.
kdgregory
The actual implementations of `FileReader` and `FileWriter` are so broken that they can throw an exception after opening the file (and because it is from the constructor, you don't have a reference to close it yourself). Also note, you are **implicitly** using whatever charset encoding happens to be configured as the default at the time, which is never a great idea.
Tom Hawtin - tackline