This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git


The following commit(s) were added to refs/heads/master by this push:
     new 7b13b4db [IO-782] SequenceReader should close readers when its close 
method is called #391.
     new b1842c0e Merge branch 'master' of 
https://gitbox.apache.org/repos/asf/commons-io.git
7b13b4db is described below

commit 7b13b4db7cec3fbf0709df90e51404345b7ad07e
Author: Gary Gregory <garydgreg...@gmail.com>
AuthorDate: Sun Oct 9 13:25:25 2022 -0400

    [IO-782] SequenceReader should close readers when its close method is
    called #391.
---
 src/changes/changes.xml                            |  3 +
 .../apache/commons/io/input/SequenceReader.java    | 39 +++++++---
 .../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();
@@ -64,6 +92,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()) {

Reply via email to