4358774: Add null InputStream and OutputStream
Reviewed-by: alanb, prappo, reinhapa, rriggs
--- a/src/java.base/share/classes/java/io/InputStream.java Fri Jan 12 11:06:22 2018 -0800
+++ b/src/java.base/share/classes/java/io/InputStream.java Fri Jan 12 11:06:24 2018 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -56,6 +56,93 @@
private static final int DEFAULT_BUFFER_SIZE = 8192;
/**
+ * Returns a new {@code InputStream} that reads no bytes. The returned
+ * stream is initially open. The stream is closed by calling the
+ * {@code close()} method. Subsequent calls to {@code close()} have no
+ * effect.
+ *
+ * <p> While the stream is open, the {@code available()}, {@code read()},
+ * {@code read(byte[])}, {@code read(byte[], int, int)},
+ * {@code readAllBytes()}, {@code readNBytes()}, {@code skip()}, and
+ * {@code transferTo()} methods all behave as if end of stream has been
+ * reached. After the stream has been closed, these methods all throw
+ * {@code IOException}.
+ *
+ * <p> The {@code markSupported()} method returns {@code false}. The
+ * {@code mark()} method does nothing, and the {@code reset()} method
+ * throws {@code IOException}.
+ *
+ * @return an {@code InputStream} which contains no bytes
+ *
+ * @since 11
+ */
+ public static InputStream nullInputStream() {
+ return new InputStream() {
+ private volatile boolean closed;
+
+ private void ensureOpen() throws IOException {
+ if (closed) {
+ throw new IOException("Stream closed");
+ }
+ }
+
+ @Override
+ public int available () throws IOException {
+ ensureOpen();
+ return 0;
+ }
+
+ @Override
+ public int read() throws IOException {
+ ensureOpen();
+ return -1;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ Objects.checkFromIndexSize(off, len, b.length);
+ if (len == 0) {
+ return 0;
+ }
+ ensureOpen();
+ return -1;
+ }
+
+ @Override
+ public byte[] readAllBytes() throws IOException {
+ ensureOpen();
+ return new byte[0];
+ }
+
+ @Override
+ public int readNBytes(byte[] b, int off, int len)
+ throws IOException {
+ Objects.checkFromIndexSize(off, len, b.length);
+ ensureOpen();
+ return 0;
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ ensureOpen();
+ return 0L;
+ }
+
+ @Override
+ public long transferTo(OutputStream out) throws IOException {
+ Objects.requireNonNull(out);
+ ensureOpen();
+ return 0L;
+ }
+
+ @Override
+ public void close() throws IOException {
+ closed = true;
+ }
+ };
+ }
+
+ /**
* Reads the next byte of data from the input stream. The value byte is
* returned as an <code>int</code> in the range <code>0</code> to
* <code>255</code>. If no byte is available because the end of the stream
@@ -166,7 +253,6 @@
* @see java.io.InputStream#read()
*/
public int read(byte b[], int off, int len) throws IOException {
- Objects.requireNonNull(b);
Objects.checkFromIndexSize(off, len, b.length);
if (len == 0) {
return 0;
@@ -326,7 +412,6 @@
* @since 9
*/
public int readNBytes(byte[] b, int off, int len) throws IOException {
- Objects.requireNonNull(b);
Objects.checkFromIndexSize(off, len, b.length);
int n = 0;
--- a/src/java.base/share/classes/java/io/OutputStream.java Fri Jan 12 11:06:22 2018 -0800
+++ b/src/java.base/share/classes/java/io/OutputStream.java Fri Jan 12 11:06:24 2018 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -47,6 +47,51 @@
*/
public abstract class OutputStream implements Closeable, Flushable {
/**
+ * Returns a new {@code OutputStream} which discards all bytes. The
+ * returned stream is initially open. The stream is closed by calling
+ * the {@code close()} method. Subsequent calls to {@code close()} have
+ * no effect.
+ *
+ * <p> While the stream is open, the {@code write(int)}, {@code
+ * write(byte[])}, and {@code write(byte[], int, int)} methods do nothing.
+ * After the stream has been closed, these methods all throw {@code
+ * IOException}.
+ *
+ * <p> The {@code flush()} method does nothing.
+ *
+ * @return an {@code OutputStream} which discards all bytes
+ *
+ * @since 11
+ */
+ public static OutputStream nullOutputStream() {
+ return new OutputStream() {
+ private volatile boolean closed;
+
+ private void ensureOpen() throws IOException {
+ if (closed) {
+ throw new IOException("Stream closed");
+ }
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ ensureOpen();
+ }
+
+ @Override
+ public void write(byte b[], int off, int len) throws IOException {
+ Objects.checkFromIndexSize(off, len, b.length);
+ ensureOpen();
+ }
+
+ @Override
+ public void close() {
+ closed = true;
+ }
+ };
+ }
+
+ /**
* Writes the specified byte to this output stream. The general
* contract for <code>write</code> is that one byte is written
* to the output stream. The byte to be written is the eight
@@ -106,7 +151,6 @@
* stream is closed.
*/
public void write(byte b[], int off, int len) throws IOException {
- Objects.requireNonNull(b);
Objects.checkFromIndexSize(off, len, b.length);
// len == 0 condition implicitly handled by loop bounds
for (int i = 0 ; i < len ; i++) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/io/InputStream/NullInputStream.java Fri Jan 12 11:06:24 2018 -0800
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import org.testng.annotations.AfterGroups;
+import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+/*
+ * @test
+ * @bug 4358774
+ * @run testng NullInputStream
+ * @summary Check for expected behavior of InputStream.nullInputStream().
+ */
+public class NullInputStream {
+ private static InputStream openStream;
+ private static InputStream closedStream;
+
+ @BeforeGroups(groups="open")
+ public static void openStream() {
+ openStream = InputStream.nullInputStream();
+ }
+
+ @BeforeGroups(groups="closed")
+ public static void openAndCloseStream() {
+ closedStream = InputStream.nullInputStream();
+ try {
+ closedStream.close();
+ } catch (IOException e) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @AfterGroups(groups="open")
+ public static void closeStream() {
+ try {
+ openStream.close();
+ } catch (IOException e) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testOpen() {
+ assertNotNull(openStream, "InputStream.nullInputStream() returned null");
+ }
+
+ @Test(groups = "open")
+ public static void testAvailable() {
+ try {
+ assertEquals(0, openStream.available(), "available() != 0");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testRead() {
+ try {
+ assertEquals(-1, openStream.read(), "read() != -1");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testReadBII() {
+ try {
+ assertEquals(-1, openStream.read(new byte[1], 0, 1),
+ "read(byte[],int,int) != -1");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testReadAllBytes() {
+ try {
+ assertEquals(0, openStream.readAllBytes().length,
+ "readAllBytes().length != 0");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testreadNBytes() {
+ try {
+ assertEquals(0, openStream.readNBytes(new byte[1], 0, 1),
+ "readNBytes(byte[],int,int) != 0");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testSkip() {
+ try {
+ assertEquals(0, openStream.skip(1), "skip() != 0");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testTransferTo() {
+ try {
+ assertEquals(0, openStream.transferTo(new ByteArrayOutputStream(7)),
+ "transferTo() != 0");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testAvailableClosed() {
+ try {
+ closedStream.available();
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testReadClosed() {
+ try {
+ closedStream.read();
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testReadBIIClosed() {
+ try {
+ closedStream.read(new byte[1], 0, 1);
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testReadAllBytesClosed() {
+ try {
+ closedStream.readAllBytes();
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testReadNBytesClosed() {
+ try {
+ closedStream.readNBytes(new byte[1], 0, 1);
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testSkipClosed() {
+ try {
+ closedStream.skip(1);
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testTransferToClosed() {
+ try {
+ closedStream.transferTo(new ByteArrayOutputStream(7));
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+}
--- a/test/jdk/java/io/InputStream/ReadParams.java Fri Jan 12 11:06:22 2018 -0800
+++ b/test/jdk/java/io/InputStream/ReadParams.java Fri Jan 12 11:06:24 2018 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 4008296 4008293 4190090 4193729
+ * @bug 4008296 4008293 4190090 4193729 4358774
* @summary Check for correct handling of parameters to
* XXXXInputStream.read(b, off, len).
*
@@ -197,6 +197,11 @@
doTest1(ifs);
ifs.close();
+ InputStream nis = InputStream.nullInputStream();
+ doTest(nis);
+ doTest1(nis);
+ nis.close();
+
/* cleanup */
fn.delete();
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/io/OutputStream/NullOutputStream.java Fri Jan 12 11:06:24 2018 -0800
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.testng.annotations.AfterGroups;
+import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+/*
+ * @test
+ * @bug 4358774
+ * @run testng NullOutputStream
+ * @summary Check for expected behavior of OutputStream.nullOutputStream().
+ */
+public class NullOutputStream {
+ private static OutputStream openStream;
+ private static OutputStream closedStream;
+
+ @BeforeGroups(groups="open")
+ public static void openStream() {
+ openStream = OutputStream.nullOutputStream();
+ }
+
+ @BeforeGroups(groups="closed")
+ public static void openAndCloseStream() {
+ closedStream = OutputStream.nullOutputStream();
+ try {
+ closedStream.close();
+ } catch (IOException e) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @AfterGroups(groups="open")
+ public static void closeStream() {
+ try {
+ openStream.close();
+ } catch (IOException e) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups="open")
+ public static void testOpen() {
+ assertNotNull(openStream,
+ "OutputStream.nullOutputStream() returned null");
+ }
+
+ @Test(groups="open")
+ public static void testWrite() {
+ try {
+ openStream.write(62832);
+ } catch (IOException e) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups="open")
+ public static void testWriteBII() {
+ try {
+ openStream.write(new byte[] {(byte)6}, 0, 1);
+ } catch (Exception e) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups="closed")
+ public static void testWriteClosed() {
+ try {
+ closedStream.write(62832);
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups="closed")
+ public static void testWriteBIIClosed() {
+ try {
+ closedStream.write(new byte[] {(byte)6}, 0, 1);
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+}
--- a/test/jdk/java/io/OutputStream/WriteParams.java Fri Jan 12 11:06:22 2018 -0800
+++ b/test/jdk/java/io/OutputStream/WriteParams.java Fri Jan 12 11:06:24 2018 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 1999, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 1267039 1267043 4193729
+ * @bug 1267039 1267043 4193729 4358774
* @summary Check for correct handling of parameters to
* XXXXOutputStream.write(b, off, len).
*
@@ -152,6 +152,11 @@
doTest1(dfos);
dfos.close();
+ OutputStream nos = OutputStream.nullOutputStream();
+ doTest(nos);
+ doTest1(nos);
+ nos.close();
+
/* cleanup */
fn.delete();