diff options
author | Gary Gregory <garydgregory@gmail.com> | 2022-10-09 13:25:25 -0400 |
---|---|---|
committer | Gary Gregory <garydgregory@gmail.com> | 2022-10-09 13:25:25 -0400 |
commit | 7b13b4db7cec3fbf0709df90e51404345b7ad07e (patch) | |
tree | cb0d7ac6c8a394364a71b793b61ae33166bff605 | |
parent | 8b13282c2449a885ba6f9413746a59cc42900c7f (diff) | |
download | apache-commons-io-7b13b4db7cec3fbf0709df90e51404345b7ad07e.tar.gz |
[IO-782] SequenceReader should close readers when its close method is
called #391.
-rw-r--r-- | src/changes/changes.xml | 3 | ||||
-rw-r--r-- | src/main/java/org/apache/commons/io/input/SequenceReader.java | 39 | ||||
-rw-r--r-- | src/test/java/org/apache/commons/io/input/SequenceReaderTest.java | 83 |
3 files changed, 113 insertions, 12 deletions
diff --git a/src/changes/changes.xml b/src/changes/changes.xml index b0b93b71..30d03657 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -210,6 +210,9 @@ The <action> type attribute can be add,update,fix,remove. <action issue="IO-611" dev="ggregory" type="fix" due-to="ArdenL-Liu, Bruno P. Kinoshita, Gary Gregory"> Better docs in IOUtils and IOUtils.byteArray(int size) #374. </action> + <action issue="IO-782" dev="ggregory" type="fix" due-to="Matteo Di Giovinazzo, Gary Gregory"> + SequenceReader should close readers when its close method is called #391. + </action> <!-- ADD --> <action type="add" dev="ggregory" due-to="Gary Gregory"> Add GitHub coverage.yml. diff --git a/src/main/java/org/apache/commons/io/input/SequenceReader.java b/src/main/java/org/apache/commons/io/input/SequenceReader.java index 9da0c3d7..514569d7 100644 --- a/src/main/java/org/apache/commons/io/input/SequenceReader.java +++ b/src/main/java/org/apache/commons/io/input/SequenceReader.java @@ -20,19 +20,24 @@ import static org.apache.commons.io.IOUtils.EOF; import java.io.IOException; import java.io.Reader; +import java.io.SequenceInputStream; +import java.io.UncheckedIOException; import java.util.Arrays; import java.util.Iterator; import java.util.Objects; /** - * Provides the contents of multiple Readers in sequence. + * Provides the contents of multiple {@link Reader}s in sequence. + * <p> + * Like {@link SequenceInputStream} but for {@link Reader} arguments. + * </p> * * @since 2.7 */ public class SequenceReader extends Reader { private Reader reader; - private Iterator<? extends Reader> readers; + private final Iterator<? extends Reader> readers; /** * Constructs a new instance with readers @@ -41,7 +46,11 @@ public class SequenceReader extends Reader { */ public SequenceReader(final Iterable<? extends Reader> readers) { this.readers = Objects.requireNonNull(readers, "readers").iterator(); - this.reader = nextReader(); + try { + this.reader = nextReader(); + } catch (final IOException e) { + throw new UncheckedIOException(e); + } } /** @@ -60,17 +69,27 @@ public class SequenceReader extends Reader { */ @Override public void close() throws IOException { - this.readers = null; - this.reader = null; + do { // NOPMD + // empty + } while (nextReader() != null); } /** * Returns the next available reader or null if done. * - * @return the next available reader or null + * @return the next available reader or null. + * @throws IOException IOException If an I/O error occurs. */ - private Reader nextReader() { - return this.readers.hasNext() ? this.readers.next() : null; + private Reader nextReader() throws IOException { + if (reader != null) { + reader.close(); + } + if (readers.hasNext()) { + reader = readers.next(); + } else { + reader = null; + } + return reader; } /* @@ -86,7 +105,7 @@ public class SequenceReader extends Reader { if (c != EOF) { break; } - reader = nextReader(); + nextReader(); } return c; } @@ -106,7 +125,7 @@ public class SequenceReader extends Reader { while (reader != null) { final int readLen = reader.read(cbuf, off, len); if (readLen == EOF) { - reader = nextReader(); + nextReader(); } else { count += readLen; off += readLen; diff --git a/src/test/java/org/apache/commons/io/input/SequenceReaderTest.java b/src/test/java/org/apache/commons/io/input/SequenceReaderTest.java index 0b4f4667..4ce2014e 100644 --- a/src/test/java/org/apache/commons/io/input/SequenceReaderTest.java +++ b/src/test/java/org/apache/commons/io/input/SequenceReaderTest.java @@ -16,9 +16,11 @@ */ package org.apache.commons.io.input; +import static org.apache.commons.io.IOUtils.EOF; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.io.Reader; @@ -26,7 +28,6 @@ import java.io.StringReader; import java.util.ArrayList; import java.util.Collection; import java.util.List; - import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; @@ -35,6 +36,33 @@ import org.junit.jupiter.api.Test; */ public class SequenceReaderTest { + private static class CustomReader extends Reader { + + boolean closed; + + protected void checkOpen() throws IOException { + if (closed) { + throw new IOException("emptyReader already closed"); + } + } + + @Override + public int read(final char[] cbuf, final int off, final int len) throws IOException { + checkOpen(); + close(); + return EOF; + } + + @Override + public void close() throws IOException { + closed = true; + } + + public boolean isClosed() { + return closed; + } + }; + private static final char NUL = 0; private void checkArray(final char[] expected, final char[] actual) { @@ -56,7 +84,7 @@ public class SequenceReaderTest { } @Test - public void testClose() throws IOException { + public void testAutoClose() throws IOException { try (Reader reader = new SequenceReader(new CharSequenceReader("FooBar"))) { checkRead(reader, "Foo"); reader.close(); @@ -65,6 +93,57 @@ public class SequenceReaderTest { } @Test + public void testClose() throws IOException { + final Reader reader = new SequenceReader(new CharSequenceReader("FooBar")); + checkRead(reader, "Foo"); + reader.close(); + checkReadEof(reader); + } + + @Test + public void testCloseReaders() throws IOException { + final CustomReader reader0 = new CustomReader(); + final CustomReader reader1 = new CustomReader() { + + private final char[] content = {'A'}; + private int position; + + @Override + public int read(final char[] cbuf, final int off, final int len) throws IOException { + checkOpen(); + + if (off < 0) { + throw new IndexOutOfBoundsException("off is negative"); + } else if (len < 0) { + throw new IndexOutOfBoundsException("len is negative"); + } else if (len > cbuf.length - off) { + throw new IndexOutOfBoundsException("len is greater than cbuf.length - off"); + } + + if (position > 0) { + return EOF; + } + + cbuf[off] = content[0]; + position++; + return 1; + } + + }; + + try (SequenceReader sequenceReader = new SequenceReader(reader1, reader0)) { + assertEquals('A', sequenceReader.read()); + assertEquals(EOF, sequenceReader.read()); + } finally { + assertTrue(reader1.isClosed()); + assertTrue(reader0.isClosed()); + } + assertTrue(reader1.isClosed()); + assertTrue(reader0.isClosed()); + + } + + @Test public void testMarkSupported() throws Exception { try (Reader reader = new SequenceReader()) { assertFalse(reader.markSupported()); |