Skip to content

Commit 5d937c9

Browse files
committed
Fix NullInputStream available(), close(), and read()
- Deprecate NullInputStream.INSTANCE in favor of constructors - close() does not close - available() should return 0 after close() - read() should return -1 after close() - add init()
1 parent 12bb455 commit 5d937c9

12 files changed

Lines changed: 203 additions & 136 deletions

src/changes/changes.xml

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ The <action> type attribute can be add,update,fix,remove.
5252
<action dev="ggregory" type="add" issue="IO-831" due-to="Elliotte Rusty Harold, Thach Le, Gary Gregory">Add getInputStream() for 'https' and 'http' in URIOrigin #630.</action>
5353
<action dev="ggregory" type="add" due-to="Gary Gregory">Add IOSupplier.getUnchecked().</action>
5454
<action dev="ggregory" type="add" due-to="Gary Gregory">Add CloseShieldInputStream.systemIn(InputStream).</action>
55+
<action dev="ggregory" type="add" due-to="Gary Gregory">Add NullInputStream.init().</action>
5556
<!-- FIX -->
5657
<action dev="ggregory" type="fix" due-to="Gary Gregory">Add missing unit tests.</action>
5758
<action dev="ggregory" type="fix" due-to="Gary Gregory">FileUtils.lastModifiedFileTime(File) calls Objects.requireNonNull() on the wrong object.</action>
@@ -61,16 +62,19 @@ The <action> type attribute can be add,update,fix,remove.
6162
<action dev="ggregory" type="fix" due-to="sullis">Add test for CircularByteBuffer clear() #620.</action>
6263
<action dev="ggregory" type="fix" due-to="Gary Gregory">PathUtils.isPosix(Path, LinkOption...) should return false on null input.</action>
6364
<action dev="ggregory" type="fix" due-to="Gary Gregory">AutoCloseInputStream(InputStream) uses ClosedInputStream.INSTANCE when its input is null.</action>
64-
<action dev="ggregory" type="add" due-to="Gary Gregory">Avoid NullPointerException in ProxyInputStream.available() when the underlying input stream is null.</action>
65-
<action dev="ggregory" type="add" due-to="Gary Gregory">BufferedFileChannelInputStream.available() returns 0 before any reads.</action>
66-
<action dev="ggregory" type="add" due-to="Gary Gregory">BufferedFileChannelInputStream.available() should return 0 when the stream is closed instead of throwing an exception.</action>
67-
<action dev="ggregory" type="add" due-to="Gary Gregory">CharSequenceInputStream.available() should return 0 after the stream is closed.</action>
68-
<action dev="ggregory" type="add" due-to="Gary Gregory">BoundedInputStream.available() should return 0 when the stream is closed.</action>
69-
<action dev="ggregory" type="add" due-to="Gary Gregory">CircularInputStream.available() should return 0 when the stream is closed.</action>
70-
<action dev="ggregory" type="add" due-to="Gary Gregory">InfiniteCircularInputStream.available() should return 0 when the stream is closed.</action>
71-
<action dev="ggregory" type="add" due-to="Gary Gregory">ChecksumInputStream(InputStream, Checksum, long, long) should fail-fast on null Checksum input.</action>
72-
<action dev="ggregory" type="add" due-to="Gary Gregory">BufferedFileChannelInputStream.read() should return -1 after the stream is closed.</action>
73-
<action dev="ggregory" type="add" due-to="Gary Gregory">BOMInputStream.read() should return -1 after the stream is closed.</action>
65+
<action dev="ggregory" type="fix" due-to="Gary Gregory">Avoid NullPointerException in ProxyInputStream.available() when the underlying input stream is null.</action>
66+
<action dev="ggregory" type="fix" due-to="Gary Gregory">BufferedFileChannelInputStream.available() returns 0 before any reads.</action>
67+
<action dev="ggregory" type="fix" due-to="Gary Gregory">BufferedFileChannelInputStream.available() should return 0 when the stream is closed instead of throwing an exception.</action>
68+
<action dev="ggregory" type="fix" due-to="Gary Gregory">CharSequenceInputStream.available() should return 0 after the stream is closed.</action>
69+
<action dev="ggregory" type="fix" due-to="Gary Gregory">BoundedInputStream.available() should return 0 when the stream is closed.</action>
70+
<action dev="ggregory" type="fix" due-to="Gary Gregory">CircularInputStream.available() should return 0 when the stream is closed.</action>
71+
<action dev="ggregory" type="fix" due-to="Gary Gregory">InfiniteCircularInputStream.available() should return 0 when the stream is closed.</action>
72+
<action dev="ggregory" type="fix" due-to="Gary Gregory">ChecksumInputStream(InputStream, Checksum, long, long) should fail-fast on null Checksum input.</action>
73+
<action dev="ggregory" type="fix" due-to="Gary Gregory">BufferedFileChannelInputStream.read() should return -1 after the stream is closed.</action>
74+
<action dev="ggregory" type="fix" due-to="Gary Gregory">BOMInputStream.read() should return -1 after the stream is closed.</action>
75+
<action dev="ggregory" type="fix" due-to="Gary Gregory">Deprecate NullInputStream.INSTANCE in favor of constructors.</action>
76+
<action dev="ggregory" type="fix" due-to="Gary Gregory">NullInputStream.available() should return 0 after the stream is closed.</action>
77+
<action dev="ggregory" type="fix" due-to="Gary Gregory">NullInputStream.read() should return -1 after the stream is closed.</action>
7478
<!-- UPDATE -->
7579
<action dev="ggregory" type="update" due-to="Dependabot">Bump tests commons.bytebuddy.version from 1.14.13 to 1.14.17 #615, #621, #631, #635.</action>
7680
<action dev="ggregory" type="update" due-to="Dependabot">Bump tests commons-codec:commons-codec from 1.16.1 to 1.17.0.</action>

src/main/java/org/apache/commons/io/input/NullInputStream.java

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import java.io.InputStream;
2424

2525
/**
26-
* A functional, light weight {@link InputStream} that emulates a stream of a specified size.
26+
* A light weight {@link InputStream} that emulates a stream of a specified size.
2727
* <p>
2828
* This implementation provides a light weight object for testing with an {@link InputStream} where the contents don't matter.
2929
* </p>
@@ -59,15 +59,21 @@ public class NullInputStream extends InputStream {
5959
/**
6060
* The singleton instance.
6161
*
62+
* <p>
63+
* Since instances hold state, call {@link #init()} to reuse.
64+
* </p>
65+
*
6266
* @since 2.12.0
67+
* @deprecated Not reusable without calling {@link #init()} to reset state.
6368
*/
69+
@Deprecated
6470
public static final NullInputStream INSTANCE = new NullInputStream();
6571

6672
private final long size;
6773
private long position;
6874
private long mark = -1;
6975
private long readLimit;
70-
private boolean eof;
76+
private boolean closed;
7177
private final boolean throwEofException;
7278
private final boolean markSupported;
7379

@@ -102,13 +108,11 @@ public NullInputStream(final long size, final boolean markSupported, final boole
102108
this.throwEofException = throwEofException;
103109
}
104110

105-
/**
106-
* Returns the number of bytes that can be read.
107-
*
108-
* @return The number of bytes that can be read.
109-
*/
110111
@Override
111112
public int available() {
113+
if (closed) {
114+
return 0;
115+
}
112116
final long avail = size - position;
113117
if (avail <= 0) {
114118
return 0;
@@ -132,14 +136,13 @@ private void checkThrowEof(final String message) throws EOFException {
132136
}
133137

134138
/**
135-
* Closes this input stream and resets the internal state to the initial values.
139+
* Closes this input stream.
136140
*
137141
* @throws IOException If an error occurs.
138142
*/
139143
@Override
140144
public void close() throws IOException {
141-
eof = false;
142-
position = 0;
145+
closed = true;
143146
mark = -1;
144147
}
145148

@@ -168,11 +171,25 @@ public long getSize() {
168171
* @throws EOFException if {@code throwEofException} is set to {@code true}.
169172
*/
170173
private int handleEof() throws EOFException {
171-
eof = true;
174+
closed = true;
172175
checkThrowEof("handleEof()");
173176
return EOF;
174177
}
175178

179+
/**
180+
* Initializes or re-initializes this instance for reuse.
181+
*
182+
* @return this instance.
183+
* @since 2.17.0
184+
*/
185+
public NullInputStream init() {
186+
closed = false;
187+
position = 0;
188+
mark = -1;
189+
readLimit = 0;
190+
return this;
191+
}
192+
176193
/**
177194
* Marks the current position.
178195
*
@@ -234,7 +251,7 @@ protected void processBytes(final byte[] bytes, final int offset, final int leng
234251
*/
235252
@Override
236253
public int read() throws IOException {
237-
if (eof) {
254+
if (closed) {
238255
checkThrowEof("read()");
239256
return EOF;
240257
}
@@ -270,7 +287,7 @@ public int read(final byte[] bytes) throws IOException {
270287
*/
271288
@Override
272289
public int read(final byte[] bytes, final int offset, final int length) throws IOException {
273-
if (eof) {
290+
if (closed) {
274291
checkThrowEof("read(byte[], int, int)");
275292
return EOF;
276293
}
@@ -305,7 +322,7 @@ public synchronized void reset() throws IOException {
305322
throw new IOException("Marked position [" + mark + "] is no longer valid - passed the read limit [" + readLimit + "]");
306323
}
307324
position = mark;
308-
eof = false;
325+
closed = false;
309326
}
310327

311328
/**
@@ -318,7 +335,7 @@ public synchronized void reset() throws IOException {
318335
*/
319336
@Override
320337
public long skip(final long numberOfBytes) throws IOException {
321-
if (eof) {
338+
if (closed) {
322339
checkThrowEof("skip(long)");
323340
return EOF;
324341
}

src/test/java/org/apache/commons/io/IOUtilsCopyTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,14 @@ public void testCopy_inputStreamToOutputStream() throws Exception {
104104
@Test
105105
public void testCopy_inputStreamToOutputStream_IO84() throws Exception {
106106
final long size = (long) Integer.MAX_VALUE + (long) 1;
107-
final InputStream in = new NullInputStream(size);
107+
final NullInputStream in = new NullInputStream(size);
108108
final OutputStream out = NullOutputStream.INSTANCE;
109109

110110
// Test copy() method
111111
assertEquals(-1, IOUtils.copy(in, out));
112112

113113
// reset the input
114-
in.close();
114+
in.init();
115115

116116
// Test copyLarge() method
117117
assertEquals(size, IOUtils.copyLarge(in, out), "copyLarge()");

src/test/java/org/apache/commons/io/IOUtilsTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,14 +481,14 @@ public void testConstants() {
481481
@Test
482482
public void testConsumeInputStream() throws Exception {
483483
final long size = (long) Integer.MAX_VALUE + (long) 1;
484-
final InputStream in = new NullInputStream(size);
484+
final NullInputStream in = new NullInputStream(size);
485485
final OutputStream out = NullOutputStream.INSTANCE;
486486

487487
// Test copy() method
488488
assertEquals(-1, IOUtils.copy(in, out));
489489

490490
// reset the input
491-
in.close();
491+
in.init();
492492

493493
// Test consume() method
494494
assertEquals(size, IOUtils.consume(in), "consume()");

src/test/java/org/apache/commons/io/file/FilesUncheckTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public void deleteFixtures() throws IOException {
9696

9797
@Test
9898
public void testCopyInputStreamPathCopyOptionArray() {
99-
assertEquals(0, FilesUncheck.copy(NullInputStream.INSTANCE, NEW_FILE_PATH, PathUtils.EMPTY_COPY_OPTIONS));
99+
assertEquals(0, FilesUncheck.copy(new NullInputStream(), NEW_FILE_PATH, PathUtils.EMPTY_COPY_OPTIONS));
100100
}
101101

102102
@Test

src/test/java/org/apache/commons/io/input/CloseShieldInputStreamTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public void close() {
5555

5656
@Test
5757
public void testAvailableAfterClose() throws Exception {
58-
InputStream shadow;
58+
final InputStream shadow;
5959
try (InputStream in = CloseShieldInputStream.wrap(byteArrayInputStream)) {
6060
assertEquals(3, in.available());
6161
shadow = in;
@@ -82,7 +82,7 @@ public void testClose() throws IOException {
8282

8383
@Test
8484
public void testReadAfterCose() throws Exception {
85-
InputStream shadow;
85+
final InputStream shadow;
8686
try (InputStream in = CloseShieldInputStream.wrap(byteArrayInputStream)) {
8787
assertEquals(3, in.available());
8888
shadow = in;

src/test/java/org/apache/commons/io/input/ClosedInputStreamTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ private void assertEof(final ClosedInputStream cis) {
3737
public void testAvailableAfterClose() throws Exception {
3838
assertEquals(0, ClosedInputStream.INSTANCE.available());
3939
assertEquals(0, ClosedInputStream.INSTANCE.available());
40-
InputStream shadow;
40+
final InputStream shadow;
4141
try (InputStream in = new ClosedInputStream()) {
4242
assertEquals(0, in.available());
4343
shadow = in;
@@ -73,7 +73,7 @@ public void testRead() throws Exception {
7373
public void testReadAfterCose() throws Exception {
7474
assertEquals(0, ClosedInputStream.INSTANCE.available());
7575
assertEquals(0, ClosedInputStream.INSTANCE.available());
76-
InputStream shadow;
76+
final InputStream shadow;
7777
try (InputStream in = new ClosedInputStream()) {
7878
assertEquals(0, in.available());
7979
shadow = in;

0 commit comments

Comments
 (0)