Skip to content

Commit 2ee3a77

Browse files
committed
Add support to MessageDigestInputStream for setting a
consumer for ProxyInputStream.afterRead(int)
1 parent f31b2f3 commit 2ee3a77

File tree

3 files changed

+57
-15
lines changed

3 files changed

+57
-15
lines changed

src/changes/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ The <action> type attribute can be add,update,fix,remove.
7373
<action dev="ggregory" type="add" due-to="Gary Gregory">Add support to ThrottledInputStream for setting a consumer for ProxyInputStream.afterRead(int).</action>
7474
<action dev="ggregory" type="add" due-to="Gary Gregory">Add support to ObservableInputStream for setting a consumer for ProxyInputStream.afterRead(int).</action>
7575
<action dev="ggregory" type="add" due-to="Gary Gregory">Add support to MessageDigestCalculatingInputStream for setting a consumer for ProxyInputStream.afterRead(int).</action>
76+
<action dev="ggregory" type="add" due-to="Gary Gregory">Add support to MessageDigestInputStream for setting a consumer for ProxyInputStream.afterRead(int).</action>
7677
<!-- UPDATE -->
7778
<action dev="ggregory" type="update" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 74 to 78 #670, #676, #679, #688.</action>
7879
<action dev="ggregory" type="update" due-to="Gary Gregory">Bump commons.bytebuddy.version from 1.15.1 to 1.15.10 #672, #673, #685, #686, #694, #696, #698.</action>

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

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@
2020
import java.io.InputStream;
2121
import java.security.MessageDigest;
2222
import java.security.NoSuchAlgorithmException;
23+
import java.util.Arrays;
2324
import java.util.Objects;
2425

25-
import org.apache.commons.io.build.AbstractStreamBuilder;
26-
2726
/**
2827
* This class is an example for using an {@link ObservableInputStream}. It creates its own {@link org.apache.commons.io.input.ObservableInputStream.Observer},
2928
* which calculates a checksum using a {@link MessageDigest}, for example, a SHA-512 sum.
@@ -62,12 +61,18 @@ public final class MessageDigestInputStream extends ObservableInputStream {
6261
* <p>
6362
* You must specify a message digest algorithm name or instance.
6463
* </p>
64+
* <p>
65+
* <em>The MD5 cryptographic algorithm is weak and should not be used.</em>
66+
* </p>
6567
*
6668
* @see #get()
6769
*/
6870
// @formatter:on
69-
public static class Builder extends AbstractStreamBuilder<MessageDigestInputStream, Builder> {
71+
public static class Builder extends AbstractBuilder<Builder> {
7072

73+
/**
74+
* No default by design, call MUST set one.
75+
*/
7176
private MessageDigest messageDigest;
7277

7378
/**
@@ -97,16 +102,16 @@ public Builder() {
97102
* @throws IOException if an I/O error occurs.
98103
* @see #getInputStream()
99104
*/
100-
@SuppressWarnings("resource")
101105
@Override
102106
public MessageDigestInputStream get() throws IOException {
103-
return new MessageDigestInputStream(getInputStream(), messageDigest);
107+
setObservers(Arrays.asList(new MessageDigestMaintainingObserver(messageDigest)));
108+
return new MessageDigestInputStream(this);
104109
}
105110

106111
/**
107112
* Sets the message digest.
108113
* <p>
109-
* The MD5 cryptographic algorithm is weak and should not be used.
114+
* <em>The MD5 cryptographic algorithm is weak and should not be used.</em>
110115
* </p>
111116
*
112117
* @param messageDigest the message digest.
@@ -120,7 +125,7 @@ public Builder setMessageDigest(final MessageDigest messageDigest) {
120125
/**
121126
* Sets the name of the name of the message digest algorithm.
122127
* <p>
123-
* The MD5 cryptographic algorithm is weak and should not be used.
128+
* <em>The MD5 cryptographic algorithm is weak and should not be used.</em>
124129
* </p>
125130
*
126131
* @param algorithm the name of the algorithm. See the MessageDigest section in the
@@ -173,6 +178,9 @@ public static Builder builder() {
173178
return new Builder();
174179
}
175180

181+
/**
182+
* A non-null MessageDigest.
183+
*/
176184
private final MessageDigest messageDigest;
177185

178186
/**
@@ -181,23 +189,22 @@ public static Builder builder() {
181189
* The MD5 cryptographic algorithm is weak and should not be used.
182190
* </p>
183191
*
184-
* @param inputStream the stream to calculate the message digest for
185-
* @param messageDigest the message digest to use
192+
* @param builder A builder use to get the stream to calculate the message digest and the message digest to use
186193
* @throws NullPointerException if messageDigest is null.
187194
*/
188-
private MessageDigestInputStream(final InputStream inputStream, final MessageDigest messageDigest) {
189-
super(inputStream, new MessageDigestMaintainingObserver(messageDigest));
190-
this.messageDigest = messageDigest;
195+
private MessageDigestInputStream(final Builder builder) throws IOException {
196+
super(builder);
197+
this.messageDigest = Objects.requireNonNull(builder.messageDigest, "builder.messageDigest");
191198
}
192199

193200
/**
194-
* Gets the {@link MessageDigest}, which is being used for generating the checksum.
201+
* Gets the {@link MessageDigest}, which is being used for generating the checksum, never null.
195202
* <p>
196203
* <em>Note</em>: The checksum will only reflect the data, which has been read so far. This is probably not, what you expect. Make sure, that the complete
197204
* data has been read, if that is what you want. The easiest way to do so is by invoking {@link #consume()}.
198205
* </p>
199206
*
200-
* @return the message digest used
207+
* @return the message digest used, never null.
201208
*/
202209
public MessageDigest getMessageDigest() {
203210
return messageDigest;

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static org.junit.jupiter.api.Assertions.assertEquals;
2121
import static org.junit.jupiter.api.Assertions.assertNotEquals;
2222
import static org.junit.jupiter.api.Assertions.assertThrows;
23+
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
2324
import static org.junit.jupiter.api.Assertions.assertTrue;
2425

2526
import java.io.ByteArrayInputStream;
@@ -30,10 +31,13 @@
3031
import java.security.MessageDigest;
3132
import java.security.NoSuchAlgorithmException;
3233
import java.util.Random;
34+
import java.util.concurrent.atomic.AtomicBoolean;
3335

3436
import org.apache.commons.codec.digest.DigestUtils;
3537
import org.apache.commons.codec.digest.MessageDigestAlgorithms;
38+
import org.apache.commons.io.IOExceptionList;
3639
import org.apache.commons.io.IOUtils;
40+
import org.apache.commons.io.test.CustomIOException;
3741
import org.junit.jupiter.api.Test;
3842

3943
/**
@@ -61,6 +65,34 @@ private InputStream createInputStream(final InputStream origin) throws IOExcepti
6165
// @formatter:on
6266
}
6367

68+
@Test
69+
public void testAfterReadConsumer() throws Exception {
70+
final AtomicBoolean boolRef = new AtomicBoolean();
71+
// @formatter:off
72+
try (InputStream bounded = MessageDigestInputStream.builder()
73+
.setMessageDigest(MessageDigest.getInstance(MessageDigestAlgorithms.SHA_512))
74+
.setCharSequence("Hi")
75+
.setAfterRead(i -> boolRef.set(true))
76+
.get()) {
77+
IOUtils.consume(bounded);
78+
}
79+
// @formatter:on
80+
assertTrue(boolRef.get());
81+
// Throwing
82+
final String message = "test exception message";
83+
// @formatter:off
84+
try (InputStream bounded = MessageDigestInputStream.builder()
85+
.setMessageDigest(MessageDigest.getInstance(MessageDigestAlgorithms.SHA_512))
86+
.setCharSequence("Hi")
87+
.setAfterRead(i -> {
88+
throw new CustomIOException(message);
89+
})
90+
.get()) {
91+
assertTrue(assertThrowsExactly(IOExceptionList.class, () -> IOUtils.consume(bounded)).getMessage().contains(message));
92+
}
93+
// @formatter:on
94+
}
95+
6496
@SuppressWarnings("resource")
6597
@Test
6698
public void testAvailableAfterClose() throws Exception {
@@ -83,7 +115,9 @@ public void testAvailableAfterOpen() throws Exception {
83115

84116
@Test
85117
public void testNoDefault() throws Exception {
86-
assertThrows(IllegalStateException.class, () -> MessageDigestInputStream.builder().get());
118+
// No default by design, call MUST set a message digest
119+
// Fail-fast, no need to try to process any input origin
120+
assertThrows(NullPointerException.class, () -> MessageDigestInputStream.builder().get());
87121
assertThrows(NullPointerException.class, () -> MessageDigestInputStream.builder().setInputStream(new ByteArrayInputStream(new byte[] { 1 })).get());
88122
}
89123

0 commit comments

Comments
 (0)