-
CSR
-
Resolution: Approved
-
P4
-
None
-
behavioral
-
low
-
-
Java API
-
Implementation
Summary
Update java.util.zip.DeflaterOutputStream
to close the wrapped stream exactly once, even in cases where the wrapped stream throws an IOException while flushing remaining deflated data.
This brings the method in line with its specification, and also with the specification of the overridden method FilterOutputStream.close().
Problem
Problem A: The wrapped stream may not be closed
DeflaterOutputStream.close is specified as "Writes remaining compressed data to the output stream and closes the underlying stream".
However, the current implementation does not always close the underlying ("wrapped") stream, specifically in the case where the wrapped stream throws an IOException when data remaining in the Deflator is flushed in DeflaterOutputStream.finish().
This may cause unintended leaks of resources such as file desciptors and network sockets.
Problem B: The wrapped stream may be closed more than once
The @implSpec in FilterOutputStream.close specifies that flushing of buffers and closing of the wrapped stream should only happen once: "When not already closed, the close method of FilterOutputStream calls its flush method, and then calls the close method of its underlying output stream".
However, the current implementation does not update the boolean "closed" flag to true if an IOException is thrown during flushing.
This may cause that the wrapped stream to be closed more than once and Deflator.end() to be called more than once.
Solution
Update DeflaterOutputStream.close to:
- Move the setting of closed = true from the end of the if(!closed) block to the beginning of the block, ensuring that a close will only ever be attempted once.
- Catch, capture to a local variable and rethrow any IOException during the call to finish()
- In the finally block, check whether finish() did throw an IOException
- If finish did throw: Close the wrapped stream in a try/catch where the catch adds the IOException from the call to finish() as a suppressed exception of the IOException which happened during close. (If the two exceptions have the same identity, avoid adding the exception as suppressed on itself)
- If finish did not throw: Simply close the wrapped stream, letting any IOException propagate out of DeflaterInputStream.close.
A new test is added to cover a variation of failure mode combinations, verifying the invariant that the wrapped stream is closed exactly once.
Specification
This is strictly an implementation change. The specification is not changed.
- csr of
-
JDK-8313739 ZipOutputStream.close() should always close the wrapped stream
- Closed