From 9844c1c52b9b7b6959d4e5cc626495c83f4aa9d4 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Tue, 8 Apr 2025 06:07:44 +0000 Subject: [PATCH] 8066583: DeflaterInput/OutputStream and InflaterInput/OutputStream should explain responsibility for freeing resources Reviewed-by: liach, alanb, lancea --- .../java/util/zip/DeflaterInputStream.java | 39 ++++++- .../java/util/zip/DeflaterOutputStream.java | 57 ++++++++-- .../java/util/zip/InflaterInputStream.java | 41 ++++++- .../java/util/zip/InflaterOutputStream.java | 35 +++++- .../java/util/zip/DeflateIn_InflateOut.java | 84 +++++++++++++- .../java/util/zip/InflateIn_DeflateOut.java | 104 ++++++++++++++++++ 6 files changed, 344 insertions(+), 16 deletions(-) diff --git a/src/java.base/share/classes/java/util/zip/DeflaterInputStream.java b/src/java.base/share/classes/java/util/zip/DeflaterInputStream.java index cb3f534cd5e..59364980598 100644 --- a/src/java.base/share/classes/java/util/zip/DeflaterInputStream.java +++ b/src/java.base/share/classes/java/util/zip/DeflaterInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,20 @@ import java.util.Objects; * Implements an input stream filter for compressing data in the "deflate" * compression format. * + *

Compressor Usage

+ * A {@code DeflaterInputStream} created without + * specifying a {@linkplain Deflater compressor} will create a compressor + * at construction time, and close the compressor when the input stream + * is {@linkplain #close closed}. + *

+ * If a compressor is specified when creating a {@code DeflaterInputStream}, it is the + * responsibility of the caller to {@linkplain Deflater#close close} the + * compressor after closing the input stream. + * + * @apiNote + * The {@link #close} method should be called to release resources used by this + * stream, either directly, or with the {@code try}-with-resources statement. + * * @since 1.6 * @author David R Tribble (david@tribble.com) * @@ -68,8 +82,11 @@ public class DeflaterInputStream extends FilterInputStream { } /** - * Creates a new input stream with a default compressor and buffer - * size. + * Creates a new input stream and compressor with the + * default compression level and a default buffer size. + *

+ * The compressor will be closed when this input stream + * is {@linkplain #close() closed}. * * @param in input stream to read the uncompressed data to * @throws NullPointerException if {@code in} is null @@ -82,6 +99,10 @@ public class DeflaterInputStream extends FilterInputStream { /** * Creates a new input stream with the specified compressor and a * default buffer size. + *

+ * {@linkplain #close() Closing} this input stream + * {@linkplain ##compressor-usage will not close} the given + * {@linkplain Deflater compressor}. * * @param in input stream to read the uncompressed data to * @param defl compressor ("deflater") for this stream @@ -94,6 +115,10 @@ public class DeflaterInputStream extends FilterInputStream { /** * Creates a new input stream with the specified compressor and buffer * size. + *

+ * {@linkplain #close() Closing} this input stream + * {@linkplain ##compressor-usage will not close} the given + * {@linkplain Deflater compressor}. * * @param in input stream to read the uncompressed data to * @param defl compressor ("deflater") for this stream @@ -123,6 +148,7 @@ public class DeflaterInputStream extends FilterInputStream { * * @throws IOException if an I/O error occurs */ + @Override public void close() throws IOException { if (in != null) { try { @@ -147,6 +173,7 @@ public class DeflaterInputStream extends FilterInputStream { * @throws IOException if an I/O error occurs or if this stream is * already closed */ + @Override public int read() throws IOException { // Read a single byte of compressed data int len = read(rbuf, 0, 1); @@ -169,6 +196,7 @@ public class DeflaterInputStream extends FilterInputStream { * @throws IOException if an I/O error occurs or if this input stream is * already closed */ + @Override public int read(byte[] b, int off, int len) throws IOException { // Sanity checks ensureOpen(); @@ -224,6 +252,7 @@ public class DeflaterInputStream extends FilterInputStream { * already closed * @throws IllegalArgumentException if {@code n < 0} */ + @Override public long skip(long n) throws IOException { if (n < 0) { throw new IllegalArgumentException("negative skip length"); @@ -259,6 +288,7 @@ public class DeflaterInputStream extends FilterInputStream { * @throws IOException if an I/O error occurs or if this stream is * already closed */ + @Override public int available() throws IOException { ensureOpen(); if (reachEOF) { @@ -273,6 +303,7 @@ public class DeflaterInputStream extends FilterInputStream { * * @return false, always */ + @Override public boolean markSupported() { return false; } @@ -282,6 +313,7 @@ public class DeflaterInputStream extends FilterInputStream { * * @param limit maximum bytes that can be read before invalidating the position marker */ + @Override public void mark(int limit) { // Operation not supported } @@ -291,6 +323,7 @@ public class DeflaterInputStream extends FilterInputStream { * * @throws IOException always thrown */ + @Override public void reset() throws IOException { throw new IOException("mark/reset not supported"); } diff --git a/src/java.base/share/classes/java/util/zip/DeflaterOutputStream.java b/src/java.base/share/classes/java/util/zip/DeflaterOutputStream.java index 66630d5adf0..3fca6a1b759 100644 --- a/src/java.base/share/classes/java/util/zip/DeflaterOutputStream.java +++ b/src/java.base/share/classes/java/util/zip/DeflaterOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,20 @@ import java.io.IOException; * or method in this class will cause a {@link NullPointerException} to be * thrown. * + *

Compressor Usage

+ * A {@code DeflaterOutputStream} created without + * specifying a {@linkplain Deflater compressor} will create a compressor + * at construction time, and close the compressor when the output stream + * is {@linkplain #close closed}. + *

+ * If a compressor is specified when creating a {@code DeflaterOutputStream}, it is the + * responsibility of the caller to {@linkplain Deflater#close close} the + * compressor after closing the output stream. + * + * @apiNote + * The {@link #close} method should be called to release resources used by this + * stream, either directly, or with the {@code try}-with-resources statement. + * * @see Deflater * @author David Connelly * @since 1.1 @@ -63,6 +77,10 @@ public class DeflaterOutputStream extends FilterOutputStream { /** * Creates a new output stream with the specified compressor, * buffer size and flush mode. + *

+ * {@linkplain #close() Closing} this output stream + * {@linkplain ##compressor-usage will not close} the given + * {@linkplain Deflater compressor}. * * @param out the output stream * @param def the compressor ("deflater") @@ -98,7 +116,11 @@ public class DeflaterOutputStream extends FilterOutputStream { * buffer size. * *

The new output stream instance is created as if by invoking - * the 4-argument constructor DeflaterOutputStream(out, def, size, false). + * the 4-argument constructor {@code DeflaterOutputStream(out, def, size, false)}. + *

+ * {@linkplain #close() Closing} this output stream + * {@linkplain ##compressor-usage will not close} the given + * {@linkplain Deflater compressor}. * * @param out the output stream * @param def the compressor ("deflater") @@ -112,6 +134,10 @@ public class DeflaterOutputStream extends FilterOutputStream { /** * Creates a new output stream with the specified compressor, flush * mode and a default buffer size. + *

+ * {@linkplain #close() Closing} this output stream + * {@linkplain ##compressor-usage will not close} the given + * {@linkplain Deflater compressor}. * * @param out the output stream * @param def the compressor ("deflater") @@ -135,7 +161,11 @@ public class DeflaterOutputStream extends FilterOutputStream { * a default buffer size. * *

The new output stream instance is created as if by invoking - * the 3-argument constructor DeflaterOutputStream(out, def, false). + * the 3-argument constructor {@code DeflaterOutputStream(out, def, false)}. + *

+ * {@linkplain #close() Closing} this output stream + * {@linkplain ##compressor-usage will not close} the given + * {@linkplain Deflater compressor}. * * @param out the output stream * @param def the compressor ("deflater") @@ -148,8 +178,12 @@ public class DeflaterOutputStream extends FilterOutputStream { /** - * Creates a new output stream with a default compressor, a default - * buffer size and the specified flush mode. + * Creates a new output stream and compressor with the + * default compression level, a default buffer size and + * the specified flush mode. + *

+ * The compressor will be closed when this output stream + * is {@linkplain #close() closed}. * * @param out the output stream * @param syncFlush @@ -166,10 +200,14 @@ public class DeflaterOutputStream extends FilterOutputStream { } /** - * Creates a new output stream with a default compressor and buffer size. + * Creates a new output stream and compressor with the + * default compression level and a default buffer size. * *

The new output stream instance is created as if by invoking - * the 2-argument constructor DeflaterOutputStream(out, false). + * the 2-argument constructor {@code DeflaterOutputStream(out, false)}. + *

+ * The compressor will be closed when this output stream + * is {@linkplain #close() closed}. * * @param out the output stream */ @@ -184,6 +222,7 @@ public class DeflaterOutputStream extends FilterOutputStream { * @param b the byte to be written * @throws IOException if an I/O error has occurred */ + @Override public void write(int b) throws IOException { byte[] buf = new byte[1]; buf[0] = (byte)(b & 0xff); @@ -198,6 +237,7 @@ public class DeflaterOutputStream extends FilterOutputStream { * @param len the length of the data * @throws IOException if an I/O error has occurred */ + @Override public void write(byte[] b, int off, int len) throws IOException { if (def.finished()) { throw new IOException("write beyond end of stream"); @@ -239,8 +279,10 @@ public class DeflaterOutputStream extends FilterOutputStream { /** * Writes remaining compressed data to the output stream and closes the * underlying stream. + * * @throws IOException if an I/O error has occurred */ + @Override public void close() throws IOException { if (!closed) { closed = true; @@ -296,6 +338,7 @@ public class DeflaterOutputStream extends FilterOutputStream { * * @since 1.7 */ + @Override public void flush() throws IOException { if (syncFlush && !def.finished()) { int len = 0; diff --git a/src/java.base/share/classes/java/util/zip/InflaterInputStream.java b/src/java.base/share/classes/java/util/zip/InflaterInputStream.java index f13b5915a84..81dcf8246e0 100644 --- a/src/java.base/share/classes/java/util/zip/InflaterInputStream.java +++ b/src/java.base/share/classes/java/util/zip/InflaterInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,21 @@ import java.util.Objects; *

Unless otherwise noted, passing a {@code null} argument to a constructor * or method in this class will cause a {@link NullPointerException} to be * thrown. + * + *

Decompressor Usage

+ * An {@code InflaterInputStream} created without + * specifying a {@linkplain Inflater decompressor} will create a decompressor + * at construction time, and close the decompressor when the input stream + * is {@linkplain #close closed}. + *

+ * If a decompressor is specified when creating a {@code InflaterInputStream}, it is the + * responsibility of the caller to {@linkplain Inflater#close close} the + * decompressor after closing the input stream. + * + * @apiNote + * The {@link #close} method should be called to release resources used by this + * stream, either directly, or with the {@code try}-with-resources statement. + * * @see Inflater * @author David Connelly * @since 1.1 @@ -75,6 +90,11 @@ public class InflaterInputStream extends FilterInputStream { /** * Creates a new input stream with the specified decompressor and * buffer size. + *

+ * {@linkplain #close() Closing} this input stream + * {@linkplain ##decompressor-usage will not close} the given + * {@linkplain Inflater decompressor}. + * * @param in the input stream * @param inf the decompressor ("inflater") * @param size the input buffer size @@ -94,6 +114,11 @@ public class InflaterInputStream extends FilterInputStream { /** * Creates a new input stream with the specified decompressor and a * default buffer size. + *

+ * {@linkplain #close() Closing} this input stream + * {@linkplain ##decompressor-usage will not close} the given + * {@linkplain Inflater decompressor}. + * * @param in the input stream * @param inf the decompressor ("inflater") */ @@ -104,7 +129,12 @@ public class InflaterInputStream extends FilterInputStream { boolean usesDefaultInflater = false; /** - * Creates a new input stream with a default decompressor and buffer size. + * Creates a new input stream and decompressor with a + * default buffer size. + *

+ * The decompressor will be closed when this input stream + * is {@linkplain #close() closed}. + * * @param in the input stream */ public InflaterInputStream(InputStream in) { @@ -120,6 +150,7 @@ public class InflaterInputStream extends FilterInputStream { * @return the byte read, or -1 if end of compressed input is reached * @throws IOException if an I/O error has occurred */ + @Override public int read() throws IOException { ensureOpen(); return read(singleByteBuf, 0, 1) == -1 ? -1 : Byte.toUnsignedInt(singleByteBuf[0]); @@ -151,6 +182,7 @@ public class InflaterInputStream extends FilterInputStream { * @throws ZipException if a ZIP format error has occurred * @throws IOException if an I/O error has occurred */ + @Override public int read(byte[] b, int off, int len) throws IOException { ensureOpen(); if (b == null) { @@ -193,6 +225,7 @@ public class InflaterInputStream extends FilterInputStream { * @throws IOException if an I/O error occurs. * */ + @Override public int available() throws IOException { ensureOpen(); if (reachEOF) { @@ -220,6 +253,7 @@ public class InflaterInputStream extends FilterInputStream { * already closed * @throws IllegalArgumentException if {@code n < 0} */ + @Override public long skip(long n) throws IOException { if (n < 0) { throw new IllegalArgumentException("negative skip length"); @@ -246,8 +280,10 @@ public class InflaterInputStream extends FilterInputStream { /** * Closes this input stream and releases any system resources associated * with the stream. + * * @throws IOException if an I/O error has occurred */ + @Override public void close() throws IOException { if (!closed) { if (usesDefaultInflater) @@ -287,6 +323,7 @@ public class InflaterInputStream extends FilterInputStream { * @see java.io.InputStream#mark(int) * @see java.io.InputStream#reset() */ + @Override public boolean markSupported() { return false; } diff --git a/src/java.base/share/classes/java/util/zip/InflaterOutputStream.java b/src/java.base/share/classes/java/util/zip/InflaterOutputStream.java index 7b08d5b4538..31c51509a76 100644 --- 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 @@ /* - * Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,20 @@ import java.util.Objects; * Implements an output stream filter for uncompressing data stored in the * "deflate" compression format. * + *

Decompressor Usage

+ * 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}. + *

+ * If a decompressor is specified when creating a {@code InflaterOutputStream}, it is the + * responsibility of the caller to {@linkplain Inflater#close close} the + * decompressor after closing the output stream. + * + * @apiNote + * The {@link #close} method should be called to release resources used by this + * stream, either directly, or with the {@code try}-with-resources statement. + * * @since 1.6 * @author David R Tribble (david@tribble.com) * @@ -68,8 +82,11 @@ public class InflaterOutputStream extends FilterOutputStream { } /** - * Creates a new output stream with a default decompressor and buffer - * size. + * Creates a new output stream and decompressor with a + * default buffer size. + *

+ * The decompressor will be closed when this output stream + * is {@linkplain #close() closed}. * * @param out output stream to write the uncompressed data to * @throws NullPointerException if {@code out} is null @@ -82,6 +99,10 @@ public class InflaterOutputStream extends FilterOutputStream { /** * Creates a new output stream with the specified decompressor and a * default buffer size. + *

+ * {@linkplain #close() Closing} this output stream + * {@linkplain ##decompressor-usage will not close} the given + * {@linkplain Inflater decompressor}. * * @param out output stream to write the uncompressed data to * @param infl decompressor ("inflater") for this stream @@ -94,6 +115,10 @@ public class InflaterOutputStream extends FilterOutputStream { /** * Creates a new output stream with the specified decompressor and * buffer size. + *

+ * {@linkplain #close() Closing} this output stream + * {@linkplain ##decompressor-usage will not close} the given + * {@linkplain Inflater decompressor}. * * @param out output stream to write the uncompressed data to * @param infl decompressor ("inflater") for this stream @@ -123,6 +148,7 @@ public class InflaterOutputStream extends FilterOutputStream { * * @throws IOException if an I/O error occurs */ + @Override public void close() throws IOException { if (!closed) { // Complete the uncompressed output @@ -142,6 +168,7 @@ public class InflaterOutputStream extends FilterOutputStream { * @throws IOException if an I/O error occurs or this stream is already * closed */ + @Override public void flush() throws IOException { ensureOpen(); @@ -199,6 +226,7 @@ public class InflaterOutputStream extends FilterOutputStream { * closed * @throws ZipException if a compression (ZIP) format error occurs */ + @Override public void write(int b) throws IOException { // Write a single byte of data wbuf[0] = (byte) b; @@ -219,6 +247,7 @@ public class InflaterOutputStream extends FilterOutputStream { * @throws NullPointerException if {@code b} is null * @throws ZipException if a compression (ZIP) format error occurs */ + @Override public void write(byte[] b, int off, int len) throws IOException { // Sanity checks ensureOpen(); diff --git a/test/jdk/java/util/zip/DeflateIn_InflateOut.java b/test/jdk/java/util/zip/DeflateIn_InflateOut.java index 68ad0eaa036..d0b23e6b811 100644 --- a/test/jdk/java/util/zip/DeflateIn_InflateOut.java +++ b/test/jdk/java/util/zip/DeflateIn_InflateOut.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ */ import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.zip.*; @@ -270,6 +271,83 @@ public class DeflateIn_InflateOut { check(Arrays.equals(data, baos.toByteArray())); } + /** + * Verifies that when a DeflaterInputStream is constructed + * by passing a Deflater instance, then closing the DeflaterInputStream + * will not close the passed Deflater instance. + */ + private static void deflaterInputStreamDeflaterNotClosed() throws Throwable { + // some arbitrary content + final byte[] original = "foo".repeat(1024).getBytes(StandardCharsets.US_ASCII); + // run the DeflaterInputStream tests + try (final Deflater def = new Deflater()) { + try (ByteArrayInputStream bis = new ByteArrayInputStream(original); + DeflaterInputStream iis = new DeflaterInputStream(bis, def)) { + iis.readAllBytes(); + } + // verify the deflater wasn't closed - reset() will throw IllegalStateException if + // the deflater is closed + def.reset(); + + // repeat the test with the other constructor + try (ByteArrayInputStream bis = new ByteArrayInputStream(original); + DeflaterInputStream iis = new DeflaterInputStream(bis, def, 1024)) { + iis.readAllBytes(); + } + // verify the deflater wasn't closed - reset() will throw IllegalStateException if + // the deflater is closed + def.reset(); + } + } + + private static byte[] deflate(final byte[] original) { + final ByteArrayOutputStream compressedBaos = new ByteArrayOutputStream(); + try (Deflater compressor = new Deflater()) { + compressor.setInput(original); + compressor.finish(); + while (!compressor.finished()) { + byte[] tmpBuffer = new byte[1024]; + int numCompressed = compressor.deflate(tmpBuffer); + compressedBaos.write(tmpBuffer, 0, numCompressed); + } + } + return compressedBaos.toByteArray(); + } + + /** + * Verifies that when a InflaterOutputStream is constructed + * by passing a Inflater instance, then closing the InflaterOutputStream + * will not close the passed Inflater instance. + */ + private static void inflaterOutputStreamInflaterNotClosed() throws Throwable { + // some arbitrary content + final byte[] original = "bar".repeat(1024).getBytes(StandardCharsets.US_ASCII); + // deflate it + final byte[] deflated = deflate(original); + try (final Inflater infl = new Inflater()) { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + InflaterOutputStream dos = new InflaterOutputStream(bos, infl)) { + dos.write(deflated); + dos.flush(); + check(Arrays.equals(original, bos.toByteArray())); + } + // verify the inflater wasn't closed - reset() will throw IllegalStateException if + // the inflater is closed + infl.reset(); + + // repeat the test with the other constructor + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + InflaterOutputStream dos = new InflaterOutputStream(bos, infl, 1024)) { + dos.write(deflated); + dos.flush(); + check(Arrays.equals(original, bos.toByteArray())); + } + // verify the inflater wasn't closed - reset() will throw IllegalStateException if + // the inflater is closed + infl.reset(); + } + } + public static void realMain(String[] args) throws Throwable { new Random(new Date().getTime()).nextBytes(data); @@ -284,6 +362,10 @@ public class DeflateIn_InflateOut { SkipBytes(); NeedsDictionary(); + + deflaterInputStreamDeflaterNotClosed(); + + inflaterOutputStreamInflaterNotClosed(); } //--------------------- Infrastructure --------------------------- diff --git a/test/jdk/java/util/zip/InflateIn_DeflateOut.java b/test/jdk/java/util/zip/InflateIn_DeflateOut.java index e9c9c59e24e..31cdf73aaae 100644 --- a/test/jdk/java/util/zip/InflateIn_DeflateOut.java +++ b/test/jdk/java/util/zip/InflateIn_DeflateOut.java @@ -1,5 +1,6 @@ /* * Copyright 2009 Google, Inc. All Rights Reserved. + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +31,7 @@ */ import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.zip.*; @@ -293,6 +295,106 @@ public class InflateIn_DeflateOut { checkLOP(gis, gos); } + private static byte[] deflate(final byte[] original) { + final ByteArrayOutputStream compressedBaos = new ByteArrayOutputStream(); + try (Deflater compressor = new Deflater()) { + compressor.setInput(original); + compressor.finish(); + while (!compressor.finished()) { + byte[] tmpBuffer = new byte[1024]; + int numCompressed = compressor.deflate(tmpBuffer); + compressedBaos.write(tmpBuffer, 0, numCompressed); + } + } + return compressedBaos.toByteArray(); + } + + /** + * Verifies that when a InflaterInputStream is constructed + * by passing a Inflater instance, then closing the InflaterInputStream + * will not close the passed Inflater instance. + */ + private static void inflaterInputStreamInflaterNotClosed() throws Throwable { + // some arbitrary content + final byte[] original = "foo".repeat(1024).getBytes(StandardCharsets.US_ASCII); + // deflate it + final byte[] deflated = deflate(original); + // run the InflaterInputStream tests + try (final Inflater infl = new Inflater()) { + try (ByteArrayInputStream bis = new ByteArrayInputStream(deflated); + InflaterInputStream iis = new InflaterInputStream(bis, infl)) { + final byte[] inflated = iis.readAllBytes(); + check(Arrays.equals(original, inflated)); + } + // verify the inflater wasn't closed - reset() will throw IllegalStateException if + // the inflater is closed + infl.reset(); + + // repeat the test with the other constructor + try (ByteArrayInputStream bis = new ByteArrayInputStream(deflated); + InflaterInputStream iis = new InflaterInputStream(bis, infl, 1024)) { + final byte[] inflated = iis.readAllBytes(); + check(Arrays.equals(original, inflated)); + } + // verify the inflater wasn't closed - reset() will throw IllegalStateException if + // the inflater is closed + infl.reset(); + } + } + + /** + * Verifies that when a DeflaterOutputStream is constructed + * by passing a Deflater instance, then closing the DeflaterOutputStream + * will not close the passed Deflater instance. + */ + private static void deflaterOutputStreamDeflaterNotClosed() throws Throwable { + // some arbitrary content + final byte[] data = "bar".repeat(1024).getBytes(StandardCharsets.US_ASCII); + // run the InflaterInputStream tests + try (final Deflater def = new Deflater()) { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DeflaterOutputStream dos = new DeflaterOutputStream(bos, def)) { + dos.write(data); + } + // verify the deflater wasn't closed - reset() will throw IllegalStateException if + // the deflater is closed + def.reset(); + + // repeat the test with the other constructor + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DeflaterOutputStream dos = new DeflaterOutputStream(bos, def, 1024)) { + dos.write(data); + } + // verify the deflater wasn't closed - reset() will throw IllegalStateException if + // the deflater is closed + def.reset(); + + // repeat the test with the other constructor + for (boolean syncFlush : new boolean[] {false, true}) { + System.out.println("testing with syncFlush = " + syncFlush); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DeflaterOutputStream dos = new DeflaterOutputStream(bos, def, syncFlush)) { + dos.write(data); + } + // verify the deflater wasn't closed - reset() will throw IllegalStateException if + // the deflater is closed + def.reset(); + } + + // repeat the test with the other constructor + for (boolean syncFlush : new boolean[] {false, true}) { + System.out.println("testing with syncFlush = " + syncFlush); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DeflaterOutputStream dos = new DeflaterOutputStream(bos, def, 1024, syncFlush)) { + dos.write(data); + } + // verify the deflater wasn't closed - reset() will throw IllegalStateException if + // the deflater is closed + def.reset(); + } + } + } + public static void realMain(String[] args) throws Throwable { WriteCloseRead(); WriteFlushRead(); @@ -300,6 +402,8 @@ public class InflateIn_DeflateOut { GZWriteFlushRead(); GZLineOrientedProtocol(); TestFlushableGZIPOutputStream(); + inflaterInputStreamInflaterNotClosed(); + deflaterOutputStreamDeflaterNotClosed(); } //--------------------- Infrastructure ---------------------------