Search code examples
javainputstreamresource-leak

Is it possible to close a Reader without closing the stream?


I have a method which accepts an InputStream (of binary data) and serializes it to XML. In order to do so, it wraps the stream with a base64 encoder and a Reader to convert it to character data. However, since the InputStream is passed in as a parameter, I would consider it a harmful side effect to close the stream, and the contract for Reader.close() says it would do just that. If I don't close the reader, the compiler warns me that I have a

Resource leak: reader is never closed

So, I can add a @SuppressWarnings( "resource" ) to the reader declaration, but is that the right thing to do? Am I missing something?

Here is the actual code:

/**
 * Writes base64 encoded text read from the binary stream.
 * 
 * @param binaryStream
 *            The binary stream to write from
 * @return <code>this</code> XmlWriter (for chaining)
 * @throws IOException
 */
public XmlWriter binary( InputStream binaryStream ) throws IOException {
    Reader reader = new InputStreamReader( 
            new Base64InputStream( binaryStream, true, base64LineLength, base64LineSeparator.getBytes( charset ) ) );
    int bufferSize = 2048;
    int charsRead;
    char[] buffer = new char[bufferSize];
    while ( (charsRead = reader.read( buffer, 0, bufferSize )) >= 0 ) {
        writer.write( buffer, 0, charsRead );
    }

    return this;
}

Solution

  • If you are a happy Java 7 user, try this:

    try(InputStream binaryStream = /* ... */) {
        xmlWriter.binary(binaryStream);
    }
    

    and stream is closed for you. If you can't use Java 7, I agree that it's not the responsibility of binary() method to close() the stream. Just ignore the warning and don't let tools drive your design. It's fine.

    As a last resort you can write a lightweight Reader wrapper ignoring close(), but I don't advice it as it makes following the program flow harder.

    Also let Apache Commons IO help you with IOUtils.copy():

    public XmlWriter binary( InputStream binaryStream ) throws IOException {
        Reader reader = new InputStreamReader( 
                new Base64InputStream( binaryStream, true, base64LineLength, base64LineSeparator.getBytes( charset ) ) );
        IOUtils.copy(reader, writer);
        return this;
    }