Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ed20f2a
[IO-856] Try test on all OSs for GitHub CI
garydgregory Nov 10, 2024
cb5359f
Merge remote-tracking branch 'upstream/master'
garydgregory Oct 19, 2025
3fe03be
Merge remote-tracking branch 'upstream/master'
garydgregory Nov 3, 2025
b5747b1
Merge remote-tracking branch 'upstream/master'
garydgregory Dec 7, 2025
515ae1e
Merge remote-tracking branch 'upstream/master'
garydgregory Dec 10, 2025
8192a78
Merge remote-tracking branch 'upstream/master'
garydgregory Dec 13, 2025
c780069
Merge remote-tracking branch 'upstream/master'
garydgregory Dec 14, 2025
6924d71
Merge remote-tracking branch 'upstream/master'
garydgregory Jan 1, 2026
3b77da2
Merge remote-tracking branch 'upstream/master'
garydgregory Feb 5, 2026
abdd9e1
Merge remote-tracking branch 'upstream/master'
garydgregory Feb 15, 2026
fb91287
Merge remote-tracking branch 'upstream/master'
garydgregory Feb 19, 2026
b165180
Merge remote-tracking branch 'upstream/master'
garydgregory Feb 21, 2026
2dd0ddf
Merge remote-tracking branch 'upstream/master'
garydgregory Feb 21, 2026
bd3f8b5
Merge remote-tracking branch 'upstream/master'
garydgregory Feb 22, 2026
b34ea84
Merge remote-tracking branch 'upstream/master'
garydgregory Mar 25, 2026
c74f241
Merge remote-tracking branch 'upstream/master'
garydgregory Mar 25, 2026
1d2b754
Merge remote-tracking branch 'upstream/master'
garydgregory Mar 26, 2026
c884a14
Merge remote-tracking branch 'upstream/master'
garydgregory Mar 26, 2026
02cab72
Merge remote-tracking branch 'upstream/master'
garydgregory Mar 26, 2026
1a60afd
CloseShieldInputStreamTest now supports a custom close shield as a
garydgregory Mar 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
*/
package org.apache.commons.io.input;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.io.function.IOUnaryOperator;

/**
* Proxy stream that prevents the underlying input stream from being closed.
* <p>
Expand All @@ -30,6 +33,50 @@
*/
public class CloseShieldInputStream extends ProxyInputStream {

/**
* Constructs a new builder for {@link CloseShieldInputStream}.
*
* @since 2.22.0
*/
public static class Builder extends AbstractBuilder<CloseShieldInputStream, Builder> {

private IOUnaryOperator<InputStream> onClose = is -> ClosedInputStream.INSTANCE;

/**
* Constructs a new instance.
*/
public Builder() {
// empty
}

@Override
public CloseShieldInputStream get() throws IOException {
return new CloseShieldInputStream(this);
}

/**
* Sets the {@code onClose} function. By default, replaces the underlying input stream when {@link #close()} is called.
*
* @param onClose the onClose function.
* @return {@code this} instance.
*/
public Builder setOnClose(final IOUnaryOperator<InputStream> onClose) {
this.onClose = onClose;
return asThis();
}

}

/**
* Constructs a new builder for {@link CloseShieldInputStream}.
*
* @return the new builder.
* @since 2.22.0
*/
public static Builder builder() {
return new Builder();
}

/**
* Constructs a proxy that only shields {@link System#in} from closing.
*
Expand All @@ -52,6 +99,13 @@ public static CloseShieldInputStream wrap(final InputStream inputStream) {
return new CloseShieldInputStream(inputStream);
}

private final IOUnaryOperator<InputStream> onClose;

private CloseShieldInputStream(final Builder builder) throws IOException {
super(builder.getInputStream());
this.onClose = builder.onClose;
}

/**
* Constructs a proxy that shields the given input stream from being closed.
*
Expand All @@ -63,16 +117,21 @@ public static CloseShieldInputStream wrap(final InputStream inputStream) {
@Deprecated
public CloseShieldInputStream(final InputStream inputStream) {
super(inputStream);
this.onClose = builder().onClose;
}

/**
* Replaces the underlying input stream with a {@link ClosedInputStream}
* sentinel. The original input stream will remain open, but this proxy will
* appear closed.
* Applies the {@code onClose} function to the underlying input stream, replacing it with the result.
* <p>
* By default, replaces the underlying input stream with a {@link ClosedInputStream} sentinel. The original input stream will remain open, but this proxy
* will appear closed.
* </p>
*
* @throws IOException Thrown by the {@code onClose} function.
*/
@Override
public void close() {
in = ClosedInputStream.INSTANCE;
public void close() throws IOException {
in = onClose.apply(in);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayInputStream;
Expand Down Expand Up @@ -79,6 +80,32 @@ void testClose() throws IOException {
assertEquals(data[0], byteArrayInputStream.read(), "read()");
}

@Test
void testOnClose() throws Exception {
try (InputStream in = CloseShieldInputStream.builder().setInputStream(byteArrayInputStream).setOnClose(is -> {
assertFalse(closed);
closed = true;
return ClosedInputStream.INSTANCE;
}).get()) {
assertEquals(3, in.available());
}
assertTrue(closed);
}

@Test
void testOnCloseException() throws Exception {
final String message = "test";
assertEquals("test", assertThrowsExactly(IOException.class, () -> {
try (InputStream in = CloseShieldInputStream.builder().setInputStream(byteArrayInputStream).setOnClose(is -> {
assertFalse(closed);
throw new IOException(message);
}).get()) {
assertEquals("test", assertThrowsExactly(IOException.class, in::close).getMessage());
}
}).getMessage());
assertFalse(closed);
}

@Test
void testReadAfterCose() throws Exception {
final InputStream shadow;
Expand Down
Loading