Skip to content

Commit 0c00862

Browse files
authored
Prevent IllegalArgumentExceptions in BrokenInputStream/Reader/OutputStream/Writer (#278)
* BrokenInputStream/Reader/OutputStream/Writer use a supplier to prevent IllegalArgumentExceptions in try-with-resources * Processed feedback from PR
1 parent 25305df commit 0c00862

8 files changed

Lines changed: 151 additions & 49 deletions

File tree

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

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.io.IOException;
2020
import java.io.InputStream;
21+
import java.util.function.Supplier;
2122

2223
/**
2324
* Always throws an {@link IOException} from all the {@link InputStream} methods where the exception is declared.
@@ -37,24 +38,34 @@ public class BrokenInputStream extends InputStream {
3738
public static final BrokenInputStream INSTANCE = new BrokenInputStream();
3839

3940
/**
40-
* The exception that is thrown by all methods of this class.
41+
* A supplier for the exception that is thrown by all methods of this class.
4142
*/
42-
private final IOException exception;
43+
private final Supplier<IOException> exceptionSupplier;
4344

4445
/**
45-
* Creates a new stream that always throws an {@link IOException}
46+
* Creates a new stream that always throws an {@link IOException}.
4647
*/
4748
public BrokenInputStream() {
48-
this(new IOException("Broken input stream"));
49+
this(() -> new IOException("Broken input stream"));
4950
}
5051

5152
/**
5253
* Creates a new stream that always throws the given exception.
5354
*
54-
* @param exception the exception to be thrown
55+
* @param exception the exception to be thrown.
5556
*/
5657
public BrokenInputStream(final IOException exception) {
57-
this.exception = exception;
58+
this(() -> exception);
59+
}
60+
61+
/**
62+
* Creates a new stream that always throws an {@link IOException}.
63+
*
64+
* @param exceptionSupplier a supplier for the exception to be thrown.
65+
* @since 2.12.0
66+
*/
67+
public BrokenInputStream(final Supplier<IOException> exceptionSupplier) {
68+
this.exceptionSupplier = exceptionSupplier;
5869
}
5970

6071
/**
@@ -65,7 +76,7 @@ public BrokenInputStream(final IOException exception) {
6576
*/
6677
@Override
6778
public int available() throws IOException {
68-
throw exception;
79+
throw exceptionSupplier.get();
6980
}
7081

7182
/**
@@ -75,7 +86,7 @@ public int available() throws IOException {
7586
*/
7687
@Override
7788
public void close() throws IOException {
78-
throw exception;
89+
throw exceptionSupplier.get();
7990
}
8091

8192
/**
@@ -86,7 +97,7 @@ public void close() throws IOException {
8697
*/
8798
@Override
8899
public int read() throws IOException {
89-
throw exception;
100+
throw exceptionSupplier.get();
90101
}
91102

92103
/**
@@ -96,7 +107,7 @@ public int read() throws IOException {
96107
*/
97108
@Override
98109
public synchronized void reset() throws IOException {
99-
throw exception;
110+
throw exceptionSupplier.get();
100111
}
101112

102113
/**
@@ -108,7 +119,7 @@ public synchronized void reset() throws IOException {
108119
*/
109120
@Override
110121
public long skip(final long n) throws IOException {
111-
throw exception;
122+
throw exceptionSupplier.get();
112123
}
113124

114125
}

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

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.io.IOException;
2020
import java.io.Reader;
21+
import java.util.function.Supplier;
2122

2223
/**
2324
* Always throws an {@link IOException} from all the {@link Reader} methods where the exception is declared.
@@ -37,24 +38,34 @@ public class BrokenReader extends Reader {
3738
public static final BrokenReader INSTANCE = new BrokenReader();
3839

3940
/**
40-
* The exception that is thrown by all methods of this class.
41+
* A supplier for the exception that is thrown by all methods of this class.
4142
*/
42-
private final IOException exception;
43+
private final Supplier<IOException> exceptionSupplier;
4344

4445
/**
45-
* Creates a new reader that always throws an {@link IOException}
46+
* Creates a new reader that always throws an {@link IOException}.
4647
*/
4748
public BrokenReader() {
48-
this(new IOException("Broken reader"));
49+
this(() -> new IOException("Broken reader"));
4950
}
5051

5152
/**
5253
* Creates a new reader that always throws the given exception.
5354
*
54-
* @param exception the exception to be thrown
55+
* @param exception the exception to be thrown.
5556
*/
5657
public BrokenReader(final IOException exception) {
57-
this.exception = exception;
58+
this(() -> exception);
59+
}
60+
61+
/**
62+
* Creates a new reader that always throws an {@link IOException}
63+
*
64+
* @param exceptionSupplier a supplier for the exception to be thrown.
65+
* @since 2.12.0
66+
*/
67+
public BrokenReader(final Supplier<IOException> exceptionSupplier) {
68+
this.exceptionSupplier = exceptionSupplier;
5869
}
5970

6071
/**
@@ -64,7 +75,7 @@ public BrokenReader(final IOException exception) {
6475
*/
6576
@Override
6677
public void close() throws IOException {
67-
throw exception;
78+
throw exceptionSupplier.get();
6879
}
6980

7081
/**
@@ -75,7 +86,7 @@ public void close() throws IOException {
7586
*/
7687
@Override
7788
public void mark(final int readAheadLimit) throws IOException {
78-
throw exception;
89+
throw exceptionSupplier.get();
7990
}
8091

8192
/**
@@ -89,7 +100,7 @@ public void mark(final int readAheadLimit) throws IOException {
89100
*/
90101
@Override
91102
public int read(final char[] cbuf, final int off, final int len) throws IOException {
92-
throw exception;
103+
throw exceptionSupplier.get();
93104
}
94105

95106
/**
@@ -100,7 +111,7 @@ public int read(final char[] cbuf, final int off, final int len) throws IOExcept
100111
*/
101112
@Override
102113
public boolean ready() throws IOException {
103-
throw exception;
114+
throw exceptionSupplier.get();
104115
}
105116

106117
/**
@@ -110,7 +121,7 @@ public boolean ready() throws IOException {
110121
*/
111122
@Override
112123
public synchronized void reset() throws IOException {
113-
throw exception;
124+
throw exceptionSupplier.get();
114125
}
115126

116127
/**
@@ -122,7 +133,7 @@ public synchronized void reset() throws IOException {
122133
*/
123134
@Override
124135
public long skip(final long n) throws IOException {
125-
throw exception;
136+
throw exceptionSupplier.get();
126137
}
127138

128139
}

src/main/java/org/apache/commons/io/output/BrokenOutputStream.java

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.io.IOException;
2020
import java.io.OutputStream;
21+
import java.util.function.Supplier;
2122

2223
/**
2324
* Broken output stream. This stream always throws an {@link IOException} from
@@ -32,24 +33,41 @@
3233
public class BrokenOutputStream extends OutputStream {
3334

3435
/**
35-
* The exception that is thrown by all methods of this class.
36+
* A singleton instance.
37+
*
38+
* @since 2.12.0
3639
*/
37-
private final IOException exception;
40+
public static final BrokenOutputStream INSTANCE = new BrokenOutputStream();
3841

3942
/**
40-
* Creates a new stream that always throws an {@link IOException}
43+
* A supplier for the exception that is thrown by all methods of this class.
44+
*/
45+
private final Supplier<IOException> exceptionSupplier;
46+
47+
/**
48+
* Creates a new stream that always throws an {@link IOException}.
4149
*/
4250
public BrokenOutputStream() {
43-
this(new IOException("Broken output stream"));
51+
this(() -> new IOException("Broken output stream"));
4452
}
4553

4654
/**
4755
* Creates a new stream that always throws the given exception.
4856
*
49-
* @param exception the exception to be thrown
57+
* @param exception the exception to be thrown.
5058
*/
5159
public BrokenOutputStream(final IOException exception) {
52-
this.exception = exception;
60+
this(() -> exception);
61+
}
62+
63+
/**
64+
* Creates a new stream that always throws an {@link IOException}.
65+
*
66+
* @param exceptionSupplier a supplier for the exception to be thrown.
67+
* @since 2.12.0
68+
*/
69+
public BrokenOutputStream(final Supplier<IOException> exceptionSupplier) {
70+
this.exceptionSupplier = exceptionSupplier;
5371
}
5472

5573
/**
@@ -59,7 +77,7 @@ public BrokenOutputStream(final IOException exception) {
5977
*/
6078
@Override
6179
public void close() throws IOException {
62-
throw exception;
80+
throw exceptionSupplier.get();
6381
}
6482

6583
/**
@@ -69,7 +87,7 @@ public void close() throws IOException {
6987
*/
7088
@Override
7189
public void flush() throws IOException {
72-
throw exception;
90+
throw exceptionSupplier.get();
7391
}
7492

7593
/**
@@ -80,7 +98,7 @@ public void flush() throws IOException {
8098
*/
8199
@Override
82100
public void write(final int b) throws IOException {
83-
throw exception;
101+
throw exceptionSupplier.get();
84102
}
85103

86104
}

src/main/java/org/apache/commons/io/output/BrokenWriter.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.io.IOException;
2020
import java.io.Writer;
21+
import java.util.function.Supplier;
2122

2223
/**
2324
* Always throws an {@link IOException} from all {@link Writer} methods.
@@ -37,24 +38,34 @@ public class BrokenWriter extends Writer {
3738
public static final BrokenWriter INSTANCE = new BrokenWriter();
3839

3940
/**
40-
* The exception that is thrown by all methods of this class.
41+
* A supplier for the exception that is thrown by all methods of this class.
4142
*/
42-
private final IOException exception;
43+
private final Supplier<IOException> exceptionSupplier;
4344

4445
/**
45-
* Creates a new writer that always throws an {@link IOException}
46+
* Creates a new writer that always throws an {@link IOException}.
4647
*/
4748
public BrokenWriter() {
48-
this(new IOException("Broken writer"));
49+
this(() -> new IOException("Broken writer"));
4950
}
5051

5152
/**
5253
* Creates a new writer that always throws the given exception.
5354
*
54-
* @param exception the exception to be thrown
55+
* @param exception the exception to be thrown.
5556
*/
5657
public BrokenWriter(final IOException exception) {
57-
this.exception = exception;
58+
this(() -> exception);
59+
}
60+
61+
/**
62+
* Creates a new writer that always throws an {@link IOException}.
63+
*
64+
* @param exceptionSupplier a supplier for the exception to be thrown.
65+
* @since 2.12.0
66+
*/
67+
public BrokenWriter(final Supplier<IOException> exceptionSupplier) {
68+
this.exceptionSupplier = exceptionSupplier;
5869
}
5970

6071
/**
@@ -64,7 +75,7 @@ public BrokenWriter(final IOException exception) {
6475
*/
6576
@Override
6677
public void close() throws IOException {
67-
throw exception;
78+
throw exceptionSupplier.get();
6879
}
6980

7081
/**
@@ -74,7 +85,7 @@ public void close() throws IOException {
7485
*/
7586
@Override
7687
public void flush() throws IOException {
77-
throw exception;
88+
throw exceptionSupplier.get();
7889
}
7990

8091
/**
@@ -87,7 +98,7 @@ public void flush() throws IOException {
8798
*/
8899
@Override
89100
public void write(final char[] cbuf, final int off, final int len) throws IOException {
90-
throw exception;
101+
throw exceptionSupplier.get();
91102
}
92103

93104
}

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818

1919
import static org.junit.jupiter.api.Assertions.assertEquals;
2020
import static org.junit.jupiter.api.Assertions.assertThrows;
21-
2221
import java.io.IOException;
2322
import java.io.InputStream;
24-
2523
import org.junit.jupiter.api.BeforeEach;
2624
import org.junit.jupiter.api.Test;
2725

@@ -67,4 +65,18 @@ public void testSkip() {
6765
assertEquals(exception, assertThrows(IOException.class, () -> stream.skip(1)));
6866
}
6967

68+
@Test
69+
public void testTryWithResources() {
70+
final IOException thrown = assertThrows(IOException.class, () -> {
71+
try (InputStream newStream = new BrokenInputStream()) {
72+
newStream.read();
73+
}
74+
});
75+
assertEquals("Broken input stream", thrown.getMessage());
76+
77+
final Throwable[] suppressed = thrown.getSuppressed();
78+
assertEquals(1, suppressed.length);
79+
assertEquals(IOException.class, suppressed[0].getClass());
80+
assertEquals("Broken input stream", suppressed[0].getMessage());
81+
}
7082
}

0 commit comments

Comments
 (0)