Skip to content

Commit 587a035

Browse files
author
Gary Gregory
committed
Add constructor ThresholdingOutputStream(int, IOConsumer, IOFunction)
and make the class concrete.
1 parent b7df6fb commit 587a035

3 files changed

Lines changed: 97 additions & 5 deletions

File tree

src/changes/changes.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ The <action> type attribute can be add,update,fix,remove.
210210
<action dev="ggregory" type="add" due-to="Gary Gregory">
211211
Add IOConsumer.noop().
212212
</action>
213+
<action dev="ggregory" type="add" due-to="Gary Gregory">
214+
Add constructor ThresholdingOutputStream(int, IOConsumer, IOFunction) and make the class concrete.
215+
</action>
213216
<!-- UPDATES -->
214217
<action dev="ggregory" type="update" due-to="Dependabot">
215218
Update junit-jupiter from 5.6.2 to 5.7.0 #153.

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

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
import java.io.IOException;
2020
import java.io.OutputStream;
2121

22+
import org.apache.commons.io.function.IOConsumer;
23+
import org.apache.commons.io.function.IOFunction;
24+
2225
/**
2326
* An output stream which triggers an event when a specified number of bytes of data have been written to it. The event
2427
* can be used, for example, to throw an exception if a maximum has been reached, or to switch the underlying stream
@@ -32,13 +35,28 @@
3235
* when a pending write operation would cause the threshold to be exceeded.
3336
* </p>
3437
*/
35-
public abstract class ThresholdingOutputStream extends OutputStream {
38+
public class ThresholdingOutputStream extends OutputStream {
39+
40+
/**
41+
* Noop output stream getter function.
42+
*/
43+
private static IOFunction<ThresholdingOutputStream, OutputStream> NOOP_OS_GETTER = os -> NullOutputStream.NULL_OUTPUT_STREAM;
3644

3745
/**
3846
* The threshold at which the event will be triggered.
3947
*/
4048
private final int threshold;
4149

50+
/**
51+
* Accepts reaching the threshold.
52+
*/
53+
private final IOConsumer<ThresholdingOutputStream> thresholdConsumer;
54+
55+
/**
56+
* Gets the output stream.
57+
*/
58+
private final IOFunction<ThresholdingOutputStream, OutputStream> outputStreamGetter;
59+
4260
/**
4361
* The number of bytes written to the output stream.
4462
*/
@@ -55,7 +73,22 @@ public abstract class ThresholdingOutputStream extends OutputStream {
5573
* @param threshold The number of bytes at which to trigger an event.
5674
*/
5775
public ThresholdingOutputStream(final int threshold) {
76+
this(threshold, IOConsumer.noop(), NOOP_OS_GETTER);
77+
}
78+
79+
/**
80+
* Constructs an instance of this class which will trigger an event at the specified threshold.
81+
*
82+
* @param threshold The number of bytes at which to trigger an event.
83+
* @param thresholdConsumer Accepts reaching the threshold.
84+
* @param outputStreamGetter Gets the output stream.
85+
* @since 2.9.0
86+
*/
87+
public ThresholdingOutputStream(final int threshold, final IOConsumer<ThresholdingOutputStream> thresholdConsumer,
88+
final IOFunction<ThresholdingOutputStream, OutputStream> outputStreamGetter) {
5889
this.threshold = threshold;
90+
this.thresholdConsumer = thresholdConsumer == null ? IOConsumer.noop() : thresholdConsumer;
91+
this.outputStreamGetter = outputStreamGetter == null ? NOOP_OS_GETTER : outputStreamGetter;
5992
}
6093

6194
/**
@@ -116,7 +149,9 @@ public long getByteCount() {
116149
*
117150
* @throws IOException if an error occurs.
118151
*/
119-
protected abstract OutputStream getStream() throws IOException;
152+
protected OutputStream getStream() throws IOException {
153+
return outputStreamGetter.apply(this);
154+
}
120155

121156
/**
122157
* Returns the threshold, in bytes, at which an event will be triggered.
@@ -162,7 +197,9 @@ protected void setByteCount(final long count) {
162197
*
163198
* @throws IOException if an error occurs.
164199
*/
165-
protected abstract void thresholdReached() throws IOException;
200+
protected void thresholdReached() throws IOException {
201+
thresholdConsumer.accept(this);
202+
}
166203

167204
/**
168205
* Writes {@code b.length} bytes from the specified byte array to this output stream.

src/test/java/org/apache/commons/io/output/ThresholdingOutputStreamTest.java

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package org.apache.commons.io.output;
1919

2020
import static org.junit.jupiter.api.Assertions.assertFalse;
21+
import static org.junit.jupiter.api.Assertions.assertThrows;
2122
import static org.junit.jupiter.api.Assertions.assertTrue;
2223

2324
import java.io.IOException;
@@ -46,10 +47,61 @@ protected void thresholdReached() throws IOException {
4647
reached.set(true);
4748
}
4849
}) {
49-
tos.write(12);
50+
tos.write('a');
5051
assertFalse(reached.get());
51-
tos.write(12);
52+
tos.write('a');
5253
assertTrue(reached.get());
5354
}
5455
}
56+
57+
@Test
58+
public void testThresholdIOConsumer() throws Exception {
59+
final AtomicBoolean reached = new AtomicBoolean();
60+
// Null threshold consumer
61+
reached.set(false);
62+
try (final ThresholdingOutputStream tos = new ThresholdingOutputStream(1, null,
63+
os -> new ByteArrayOutputStream(4))) {
64+
tos.write('a');
65+
assertFalse(reached.get());
66+
tos.write('a');
67+
assertFalse(reached.get());
68+
}
69+
// Null output stream function
70+
reached.set(false);
71+
try (final ThresholdingOutputStream tos = new ThresholdingOutputStream(1, os -> reached.set(true), null)) {
72+
tos.write('a');
73+
assertFalse(reached.get());
74+
tos.write('a');
75+
assertTrue(reached.get());
76+
}
77+
// non-null inputs.
78+
reached.set(false);
79+
try (final ThresholdingOutputStream tos = new ThresholdingOutputStream(1, os -> reached.set(true),
80+
os -> new ByteArrayOutputStream(4))) {
81+
tos.write('a');
82+
assertFalse(reached.get());
83+
tos.write('a');
84+
assertTrue(reached.get());
85+
}
86+
}
87+
88+
@Test
89+
public void testThresholdIOConsumerIOException() throws Exception {
90+
try (final ThresholdingOutputStream tos = new ThresholdingOutputStream(1, os -> {
91+
throw new IOException("Threshold reached.");
92+
}, os -> new ByteArrayOutputStream(4))) {
93+
tos.write('a');
94+
assertThrows(IOException.class, () -> tos.write('a'));
95+
}
96+
}
97+
98+
@Test
99+
public void testThresholdIOConsumerUncheckedException() throws Exception {
100+
try (final ThresholdingOutputStream tos = new ThresholdingOutputStream(1, os -> {
101+
throw new IllegalStateException("Threshold reached.");
102+
}, os -> new ByteArrayOutputStream(4))) {
103+
tos.write('a');
104+
assertThrows(IllegalStateException.class, () -> tos.write('a'));
105+
}
106+
}
55107
}

0 commit comments

Comments
 (0)