aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Gregory <garydgregory@gmail.com>2022-10-09 13:25:25 -0400
committerGary Gregory <garydgregory@gmail.com>2022-10-09 13:25:25 -0400
commit7b13b4db7cec3fbf0709df90e51404345b7ad07e (patch)
treecb0d7ac6c8a394364a71b793b61ae33166bff605
parent8b13282c2449a885ba6f9413746a59cc42900c7f (diff)
downloadapache-commons-io-7b13b4db7cec3fbf0709df90e51404345b7ad07e.tar.gz
[IO-782] SequenceReader should close readers when its close method is
called #391.
-rw-r--r--src/changes/changes.xml3
-rw-r--r--src/main/java/org/apache/commons/io/input/SequenceReader.java39
-rw-r--r--src/test/java/org/apache/commons/io/input/SequenceReaderTest.java83
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());