-
Type:
CSR
-
Resolution: Approved
-
Priority:
P3
-
Component/s: core-libs
-
None
-
behavioral
-
minimal
-
The change updates the specification of the methods in InflaterOutputStream to match the current implementation. As such, this change isn't expected to cause a compatibility issue.
-
Java API
-
SE
Summary
The specification of java.util.zip.InflaterOutputStream.finish() method is updated to clarify that it will close the underlying java.util.zip.Inflater, if the InflaterOutputStream was constructed without passing an Inflater.
Problem
InflaterOutputStream can be constructed either by passing an Inflater or without passing one. If it is constructed without passing an Inflater, then the InflaterOutputStream will create an Inflater of its own. Such an Inflater that is constructed by the InflaterOutputStream will be closed when the InflaterOutputStream itself is closed. This behaviour is already part of the specification of InflaterOutputStream.
InflaterOutputStream has a finish() method which is specified to write out any buffered pending decompressed data to the underlying output stream. This method also specifies that it will not close the underlying output stream. The current implementation of finish() complies to this specification. Additionally, the current implementation closes the Inflater if the InflaterOutputStream was constructed without passing an Inflater. Once the Inflater is closed, the InflaterOutputStream can no longer be used for any further write operations. This behaviour is currently not specified. Furthermore, in the current implementation, after the Inflater is closed in finish(), subsequent calls to write() methods generate an unspecified IllegalStateException instead of throwing the specified IOException.
Solution
The specification of InflaterOutputStream.finish() is updated to match the current implementation of that method. The specification now states that the underlying Inflater will be closed if the InflaterOutputStream was constructed without passing an Inflater, and such InflaterOutputStream will not be usable for any subsequent write()s. Furthermore, the implementation of write() methods is now updated to throw the specified IOException in such cases.
A couple of alternatives were discussed and experimented. In one such alternative, the finish() method was considered for being marked for deprecation in favour of the currently existing flush() method. We decided not to deprecate finish() because the deprecation didn't seem to add much value and the possibility that there might be subclasses of InflaterOutputStream which may have extended this public method to provide a different implementation. Deprecating finish() would impact those overridden methods.
Another alternative that was considered was to not close the Inflater in the finish() method. We decided that even this is not worth doing because changing this long standing behaviour could introduce a potentially bigger compatibility impact to existing applications. These applications may have been relying on the long-standing behavior where calling finish() was sufficient to close the Inflater. In such applications if InflaterOutputStream.finish() is called but InflaterOutputStream.close() is intentionally or accidentally skipped, then no longer closing Inflater in the finish() method implementation could lead to delayed resource release.
Specification
diff --git a/src/java.base/share/classes/java/util/zip/InflaterOutputStream.java b/src/java.base/share/classes/java/util/zip/InflaterOutputStream.java
@@ -1,5 +1,5 @@
/**
- * Implements an output stream filter for uncompressing data stored in the
+ * Implements an output stream filter for decompressing data stored in the
* "deflate" compression format.
*
* <h2 id="decompressor-usage">Decompressor Usage</h2>
* An {@code InflaterOutputStream} created without
* specifying a {@linkplain Inflater decompressor} will create a decompressor
* at construction time, and close the decompressor when the output stream
- * is {@linkplain #close closed}.
+ * is {@linkplain #close closed} or when {@link #finish()} is called.
public class InflaterOutputStream extends FilterOutputStream {
- * @param out output stream to write the uncompressed data to
+ * @param out output stream to write the decompressed data to
* @throws NullPointerException if {@code out} is null
*/
public InflaterOutputStream(OutputStream out) {
- * @param out output stream to write the uncompressed data to
+ * @param out output stream to write the decompressed data to
* @param infl decompressor ("inflater") for this stream
* @throws NullPointerException if {@code out} or {@code infl} is null
*/
public InflaterOutputStream(OutputStream out, Inflater infl) {
- * @param out output stream to write the uncompressed data to
+ * @param out output stream to write the decompressed data to
* @param infl decompressor ("inflater") for this stream
* @param bufLen decompression buffer size
* @throws IllegalArgumentException if {@code bufLen <= 0}
public InflaterOutputStream(OutputStream out, Inflater infl, int bufLen) {
/**
- * Writes any remaining uncompressed data to the output stream and closes
+ * Writes any remaining decompressed data to the output stream and closes
* the underlying output stream.
*
+ * @implSpec If not already closed, this method calls {@link #finish()} before
+ * closing the underlying output stream.
+ *
* @throws IOException if an I/O error occurs
*/
@Override
public void close() throws IOException {
/**
- * Flushes this output stream, forcing any pending buffered output bytes to be
- * written.
+ * Flushes this output stream, writing any pending buffered decompressed data to
+ * the underlying output stream.
*
* @throws IOException if an I/O error occurs or this stream is already
* closed
public void flush() throws IOException {
/**
- * Finishes writing uncompressed data to the output stream without closing
- * the underlying stream. Use this method when applying multiple filters in
- * succession to the same output stream.
+ * Writes any pending buffered decompressed data to the underlying output stream,
+ * without closing the underlying stream.
*
- * @throws IOException if an I/O error occurs or this stream is already
- * closed
+ * @implSpec This method calls {@link #flush()} to write any pending buffered
+ * decompressed data.
+ * <p>
+ * If this {@code InflaterOutputStream} was created without specifying
+ * a {@linkplain Inflater decompressor}, then this method closes the decompressor
+ * that was created at construction time. The {@code InflaterOutputStream} cannot
+ * then be used for any further writes.
+ *
+ * @throws IOException if an I/O error occurs or this stream is already closed
*/
public void finish() throws IOException {
/**
- * Writes a byte to the uncompressed output stream.
+ * Writes a byte to the decompressed output stream.
*
* @param b a single byte of compressed data to decompress and write to
* the output stream
public void write(int b) throws IOException {
/**
- * Writes an array of bytes to the uncompressed output stream.
+ * Writes an array of bytes to the decompressed output stream.
*
* @param b buffer containing compressed data to decompress and write to
* the output stream
public void write(byte[] b, int off, int len) throws IOException {
- csr of
-
JDK-8369181 InflaterOutputStream: writing after finish() results in IllegalStateException instead of an IOException
-
- Open
-