| /* |
| * Copyright (c) 2012, 2013, 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. |
| */ |
| |
| /* @test |
| * @bug 7176630 7074436 |
| * @summary Check for short writes on SocketChannels configured in blocking mode |
| * @key randomness |
| */ |
| |
| import java.net.*; |
| import java.nio.ByteBuffer; |
| import java.nio.channels.*; |
| import java.util.concurrent.*; |
| import java.util.Random; |
| import java.util.zip.CRC32; |
| |
| public class ShortWrite { |
| |
| static final Random rand = new Random(); |
| |
| /** |
| * Returns a checksum on the remaining bytes in the given buffers. |
| */ |
| static long computeChecksum(ByteBuffer... bufs) { |
| CRC32 crc32 = new CRC32(); |
| for (int i=0; i<bufs.length; i++) |
| crc32.update(bufs[i]); |
| return crc32.getValue(); |
| } |
| |
| /** |
| * A task that reads the expected number of bytes and returns the CRC32 |
| * of those bytes. |
| */ |
| static class Reader implements Callable<Long> { |
| final SocketChannel sc; |
| final ByteBuffer buf; |
| |
| Reader(SocketChannel sc, int expectedSize) { |
| this.sc = sc; |
| this.buf = ByteBuffer.allocate(expectedSize); |
| } |
| |
| public Long call() throws Exception { |
| while (buf.hasRemaining()) { |
| int n = sc.read(buf); |
| if (n == -1) |
| throw new RuntimeException("Premature EOF encountered"); |
| } |
| buf.flip(); |
| return computeChecksum(buf); |
| } |
| } |
| |
| /** |
| * Exercise write(ByteBuffer) with given number of bytes. |
| */ |
| static void test1(ExecutorService pool, |
| SocketChannel source, |
| SocketChannel sink, |
| int size) |
| throws Exception |
| { |
| System.out.println("write(ByteBuffer), size=" + size); |
| |
| // random bytes in the buffer |
| ByteBuffer buf = ByteBuffer.allocate(size); |
| rand.nextBytes(buf.array()); |
| |
| // submit task to read the bytes |
| Future<Long> result = pool.submit(new Reader(sink, size)); |
| |
| // write the bytes |
| int n = source.write(buf); |
| if (n != size) |
| throw new RuntimeException("Short write detected"); |
| |
| // check the bytes that were received match |
| buf.rewind(); |
| long expected = computeChecksum(buf); |
| long actual = result.get(); |
| if (actual != expected) |
| throw new RuntimeException("Checksum did not match"); |
| } |
| |
| /** |
| * Exercise write(ByteBuffer[]) with buffers of the given sizes. |
| */ |
| static void testN(ExecutorService pool, |
| SocketChannel source, |
| SocketChannel sink, |
| int... sizes) |
| throws Exception |
| { |
| System.out.print("write(ByteBuffer[]), sizes="); |
| for (int size: sizes) |
| System.out.print(size + " "); |
| System.out.println(); |
| |
| int total = 0; |
| int len = sizes.length; |
| ByteBuffer[] bufs = new ByteBuffer[len]; |
| for (int i=0; i<len; i++) { |
| int size = sizes[i]; |
| ByteBuffer buf = ByteBuffer.allocate(size); |
| rand.nextBytes(buf.array()); |
| bufs[i] = buf; |
| total += size; |
| } |
| |
| // submit task to read the bytes |
| Future<Long> result = pool.submit(new Reader(sink, total)); |
| |
| // write the bytes |
| long n = source.write(bufs); |
| if (n != total) |
| throw new RuntimeException("Short write detected"); |
| |
| // check the bytes that were received match |
| for (int i=0; i<len; i++) |
| bufs[i].rewind(); |
| long expected = computeChecksum(bufs); |
| long actual = result.get(); |
| if (actual != expected) |
| throw new RuntimeException("Checksum did not match"); |
| } |
| |
| public static void main(String[] args) throws Exception { |
| ExecutorService pool = Executors.newSingleThreadExecutor(); |
| try { |
| try (ServerSocketChannel ssc = ServerSocketChannel.open()) { |
| ssc.bind(new InetSocketAddress(0)); |
| InetAddress lh = InetAddress.getLocalHost(); |
| int port = ssc.socket().getLocalPort(); |
| SocketAddress sa = new InetSocketAddress(lh, port); |
| |
| try (SocketChannel source = SocketChannel.open(sa); |
| SocketChannel sink = ssc.accept()) |
| { |
| // Exercise write(BufferBuffer) on sizes around 128k |
| int BOUNDARY = 128 * 1024; |
| for (int size=(BOUNDARY-2); size<=(BOUNDARY+2); size++) { |
| test1(pool, source, sink, size); |
| } |
| |
| // Exercise write(BufferBuffer) on random sizes |
| for (int i=0; i<20; i++) { |
| int size = rand.nextInt(1024*1024); |
| test1(pool, source, sink, size); |
| } |
| |
| // Exercise write(BufferBuffer[]) on sizes around 128k |
| for (int i=BOUNDARY-2; i<=BOUNDARY+2; i++) { |
| testN(pool, source, sink, i); |
| testN(pool, source, sink, 0, i); |
| testN(pool, source, sink, i, 0); |
| for (int j=BOUNDARY-2; j<=BOUNDARY+2; j++) { |
| testN(pool, source, sink, i, j); |
| testN(pool, source, sink, 0, i, j); |
| testN(pool, source, sink, i, 0, j); |
| testN(pool, source, sink, i, j, 0); |
| for (int k=BOUNDARY-2; k<=BOUNDARY+2; k++) { |
| testN(pool, source, sink, i, j, k); |
| testN(pool, source, sink, 0, i, j, k); |
| testN(pool, source, sink, i, 0, j, k); |
| testN(pool, source, sink, i, j, 0, k); |
| testN(pool, source, sink, i, j, k, 0); |
| } |
| } |
| } |
| |
| // Exercise write(BufferBuffer[]) on random sizes |
| // (assumes IOV_MAX >= 8) |
| for (int i=0; i<20; i++) { |
| int n = rand.nextInt(9); |
| int[] sizes = new int[n]; |
| for (int j=0; j<n; j++) { |
| sizes[j] = rand.nextInt(1024*1024); |
| } |
| testN(pool, source, sink, sizes); |
| } |
| } |
| } |
| |
| } finally { |
| pool.shutdown(); |
| } |
| } |
| } |