Browse Source

Add support for non-flushing OutputStream

Avoids decoration for streams that are non-closing/flushing already.

Closes gh-36382
pull/28225/merge
Juergen Hoeller 4 weeks ago
parent
commit
ab8cb8de6e
  1. 47
      spring-core/src/main/java/org/springframework/util/StreamUtils.java
  2. 29
      spring-core/src/test/java/org/springframework/util/StreamUtilsTests.java

47
spring-core/src/main/java/org/springframework/util/StreamUtils.java

@ -221,9 +221,13 @@ public abstract class StreamUtils { @@ -221,9 +221,13 @@ public abstract class StreamUtils {
* Return a variant of the given {@link InputStream} where calling
* {@link InputStream#close() close()} has no effect.
* @param in the InputStream to decorate
* @return a version of the InputStream that ignores calls to close
* @return a version of the InputStream that ignores calls to close,
* or the given InputStream if it is non-closing already
*/
public static InputStream nonClosing(InputStream in) {
if (in instanceof NonClosingInputStream) {
return in;
}
Assert.notNull(in, "No InputStream specified");
return new NonClosingInputStream(in);
}
@ -232,15 +236,38 @@ public abstract class StreamUtils { @@ -232,15 +236,38 @@ public abstract class StreamUtils {
* Return a variant of the given {@link OutputStream} where calling
* {@link OutputStream#close() close()} has no effect.
* @param out the OutputStream to decorate
* @return a version of the OutputStream that ignores calls to close
* @return a version of the OutputStream that ignores calls to close,
* or the given OutputStream if it is non-closing already
* @see #nonFlushing(OutputStream)
*/
public static OutputStream nonClosing(OutputStream out) {
if (out instanceof NonClosingOutputStream) {
return out;
}
Assert.notNull(out, "No OutputStream specified");
return new NonClosingOutputStream(out);
}
/**
* Return a variant of the given {@link OutputStream} where calling
* {@link OutputStream#flush() flush()} and/or
* {@link OutputStream#close() close()} has no effect.
* @param out the OutputStream to decorate
* @return a version of the OutputStream that ignores calls to flush/close,
* or the given OutputStream if it is non-flushing already
* @since 7.0.6
* @see #nonClosing(OutputStream)
*/
public static OutputStream nonFlushing(OutputStream out) {
if (out instanceof NonFlushingOutputStream) {
return out;
}
Assert.notNull(out, "No OutputStream specified");
return new NonFlushingOutputStream(out);
}
private static final class NonClosingInputStream extends FilterInputStream {
private static class NonClosingInputStream extends FilterInputStream {
public NonClosingInputStream(InputStream in) {
super(in);
@ -272,7 +299,7 @@ public abstract class StreamUtils { @@ -272,7 +299,7 @@ public abstract class StreamUtils {
}
private static final class NonClosingOutputStream extends FilterOutputStream {
private static class NonClosingOutputStream extends FilterOutputStream {
public NonClosingOutputStream(OutputStream out) {
super(out);
@ -289,4 +316,16 @@ public abstract class StreamUtils { @@ -289,4 +316,16 @@ public abstract class StreamUtils {
}
}
private static class NonFlushingOutputStream extends NonClosingOutputStream {
public NonFlushingOutputStream(OutputStream out) {
super(out);
}
@Override
public void flush() throws IOException {
}
}
}

29
spring-core/src/test/java/org/springframework/util/StreamUtilsTests.java

@ -124,6 +124,7 @@ class StreamUtilsTests { @@ -124,6 +124,7 @@ class StreamUtilsTests {
void nonClosingInputStream() throws Exception {
InputStream source = mock();
InputStream nonClosing = StreamUtils.nonClosing(source);
nonClosing.read();
nonClosing.read(bytes);
nonClosing.read(bytes, 1, 2);
@ -133,21 +134,49 @@ class StreamUtilsTests { @@ -133,21 +134,49 @@ class StreamUtilsTests {
ordered.verify(source).read(bytes, 0, bytes.length);
ordered.verify(source).read(bytes, 1, 2);
ordered.verify(source, never()).close();
assertThat(StreamUtils.nonClosing(nonClosing)).isSameAs(nonClosing);
}
@Test
void nonClosingOutputStream() throws Exception {
OutputStream source = mock();
OutputStream nonClosing = StreamUtils.nonClosing(source);
nonClosing.write(1);
nonClosing.write(bytes);
nonClosing.write(bytes, 1, 2);
nonClosing.flush();
nonClosing.close();
InOrder ordered = inOrder(source);
ordered.verify(source).write(1);
ordered.verify(source).write(bytes, 0, bytes.length);
ordered.verify(source).write(bytes, 1, 2);
ordered.verify(source).flush();
ordered.verify(source, never()).close();
assertThat(StreamUtils.nonClosing(nonClosing)).isSameAs(nonClosing);
}
@Test
void nonFlushingOutputStream() throws Exception {
OutputStream source = mock();
OutputStream nonFlushing = StreamUtils.nonFlushing(source);
nonFlushing.write(1);
nonFlushing.write(bytes);
nonFlushing.write(bytes, 1, 2);
nonFlushing.flush();
nonFlushing.close();
InOrder ordered = inOrder(source);
ordered.verify(source).write(1);
ordered.verify(source).write(bytes, 0, bytes.length);
ordered.verify(source).write(bytes, 1, 2);
ordered.verify(source, never()).flush();
ordered.verify(source, never()).close();
assertThat(StreamUtils.nonFlushing(nonFlushing)).isSameAs(nonFlushing);
assertThat(StreamUtils.nonClosing(nonFlushing)).isSameAs(nonFlushing);
}
}

Loading…
Cancel
Save