Java 8 FilterOutputStream.close() bug
Pavel Rappo
pavel.rappo at oracle.com
Wed Dec 3 16:29:25 UTC 2014
Nathan, thanks a lot for reporting this. Sorry no one has replied to you earlier,
but your bug report was actually entered into the JBS (JDK Bug System):
https://bugs.openjdk.java.net/browse/JDK-8054565
So don't worry, it won't get lost.
Now about the issue itself. It's indeed a bug. Though methods should be written in
an atomic fashion [1] (where possible and where it makes sense), you are right --
java.io.Closeable#close is certainly not one of these methods.
There is nothing you can do if 'close' fails. This method has a terminal semantics.
Implementations cannot rely on some external retry strategy. java.io.Closeable#close
should be called at most once in a lifetime of a Closeable object.
I skimmed through the JDK sources and found some more examples of this behaviour.
They should be fixed as well. As for this particular issue, I see two options:
1. We just fix it and that's it
2. You sign OCA [2], provide a test, prepare a change review
and if everything is fine we push it with comment
'Contributed-by: nathan.a.clement at hotmail.com' (or whatever email you choose)
---------------------------------------------------------------------------------
[1] Effective Java, Second Edition, Item 64: Strive for failure atomicity
[2] http://openjdk.java.net/contribute/
-Pavel
> On 2 Dec 2014, at 23:36, Nathan Clement <nathan.a.clement at hotmail.com> wrote:
>
> Hi,
>
> I have encountered a bug in FilterOutputStream under Java 8. The bug is that calling close() on the same stream twice can now result in an exception, whereas under Java 7 it did not. The reason is that FilterOutputStream.close() calls flush() before calling close() on the underlying stream. Calling close() twice on the FilterOutputStream will thus result in flush() being called twice. Some streams (such as OracleBlobOutputStream) will throw an exception if flush() is called after the stream is closed. In Java 7 any exceptions when calling flush() were ignored, but in Java 8 these exceptions are passed on to the caller.
>
> This bug appears to have been introduced in "7015589: (spec) BufferedWriter.close leaves stream open if close of underlying Writer fails". The FilterOutputStream.close() method now does not obey the contract of AutoClosable.close(): "If the stream is already closed then invoking this method has no effect."
>
> Some sample code that illustrates the problem:
>
> try( InputStream bis = new BufferedInputStream( inputStream );
> OutputStream outStream = payloadData.setBinaryStream( 0 );
> BufferedOutputStream bos = new BufferedOutputStream( outStream );
> DeflaterOutputStream deflaterStream = new DeflaterOutputStream(
> bos, new Deflater( 3 ) ) )
> {
> fileSize = IOUtil.copy( bis, deflaterStream );
> }
> The try-with-resources mechanism will call close on the DeflaterOutputStream, which will in turn call close on the BufferedOutputStream, which will call flush() then close() on the blob output stream. The try-with-resources mechanism will then call close on the BufferedOutputStream which again calls flush() on the blob output stream, resulting in an exception.
>
> My proposed fix is to change FilterOutputStream.close() as follows:
>
> @Override
> @SuppressWarnings("try")
> public void close() throws IOException {
> if (!closed) {
> closed = true;
> try (OutputStream ostream = out) {
> flush();
> }
> }
> }
>
> private boolean closed = false;
>
> This will prevent flush() being called on an already closed stream.
>
> I have reported this issue via the Oracle bug tracker and got Review ID JI-9014085, however I never received any follow up from Oracle. I have also discussed the issue on Stack Overflow: http://stackoverflow.com/questions/25175882/java-8-filteroutputstream-exception. Do people agree that this is a bug? If so, what is the process for fixing it?
>
> Thanks,
>
> Nathan Clement
>
More information about the core-libs-dev
mailing list