1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @bug 8198372
  27  * @modules jdk.net java.base/sun.nio.ch:+open
  28  * @run testng Basic
  29  * @summary Basic tests for jdk.nio.Channels
  30  */
  31 
  32 import java.io.Closeable;
  33 import java.io.FileDescriptor;
  34 import java.io.IOException;
  35 import java.lang.reflect.Field;
  36 import java.net.InetAddress;
  37 import java.net.InetSocketAddress;
  38 import java.net.SocketAddress;
  39 import java.nio.ByteBuffer;
  40 import java.nio.channels.SelectableChannel;
  41 import java.nio.channels.SelectionKey;
  42 import java.nio.channels.Selector;
  43 import java.nio.channels.ServerSocketChannel;
  44 import java.nio.channels.SocketChannel;
  45 
  46 import jdk.nio.Channels;
  47 import jdk.nio.Channels.SelectableChannelCloser;
  48 
  49 import sun.nio.ch.IOUtil;
  50 
  51 import org.testng.annotations.Test;
  52 import static org.testng.Assert.*;
  53 
  54 @Test
  55 public class Basic {
  56 
  57     /**
  58      * A loopback connection
  59      */
  60     static class Connection implements Closeable {
  61         private final SocketChannel sc1;
  62         private final SocketChannel sc2;
  63 
  64         private Connection(SocketChannel sc1, SocketChannel sc2) {
  65             this.sc1 = sc1;
  66             this.sc2 = sc2;
  67         }
  68 
  69         static Connection open() throws IOException {
  70             try (ServerSocketChannel ssc = ServerSocketChannel.open()) {
  71                 InetAddress lb = InetAddress.getLoopbackAddress();
  72                 ssc.bind(new InetSocketAddress(lb, 0));
  73                 SocketChannel sc1 = SocketChannel.open(ssc.getLocalAddress());
  74                 SocketChannel sc2 = ssc.accept();
  75                 return new Connection(sc1, sc2);
  76             }
  77         }
  78 
  79         SocketChannel channel1() {
  80             return sc1;
  81         }
  82 
  83         SocketChannel channel2() {
  84             return sc2;
  85         }
  86 
  87         public void close() throws IOException {
  88             try {
  89                 sc1.close();
  90             } finally {
  91                 sc2.close();
  92             }
  93         }
  94     }
  95 
  96     /**
  97      * A SelectableChannelCloser that tracks if the implCloseChannel and
  98      * implReleaseChannel methods are invoked
  99      */
 100     static class Closer implements SelectableChannelCloser {
 101         int closeCount;
 102         SelectableChannel invokedToClose;
 103         int releaseCount;
 104         SelectableChannel invokedToRelease;
 105 
 106         @Override
 107         public void implCloseChannel(SelectableChannel sc) {
 108             closeCount++;
 109             invokedToClose = sc;
 110         }
 111 
 112         @Override
 113         public void implReleaseChannel(SelectableChannel sc) {
 114             releaseCount++;
 115             invokedToRelease = sc;
 116         }
 117     }
 118 
 119     /**
 120      * Basic test of channel registered with Selector
 121      */
 122     public void testSelect() throws IOException {
 123         Selector sel = Selector.open();
 124         try (Connection connection = Connection.open()) {
 125 
 126             // create channel with the file descriptor from one end of the connection
 127             FileDescriptor fd = getFD(connection.channel1());
 128             SelectableChannel ch = Channels.readWriteSelectableChannel(fd, new Closer());
 129 
 130             // register for read events, channel should not be selected
 131             ch.configureBlocking(false);
 132             SelectionKey key = ch.register(sel, SelectionKey.OP_READ);
 133             int n = sel.selectNow();
 134             assertTrue(n == 0);
 135 
 136             // write bytes to other end of connection
 137             SocketChannel peer = connection.channel2();
 138             ByteBuffer msg = ByteBuffer.wrap("hello".getBytes("UTF-8"));
 139             int nwrote = peer.write(msg);
 140             assertTrue(nwrote >= 0);
 141 
 142             // channel should be selected
 143             n = sel.select();
 144             assertTrue(n == 1);
 145             assertTrue(sel.selectedKeys().contains(key));
 146             assertTrue(key.isReadable());
 147             assertFalse(key.isWritable());
 148             sel.selectedKeys().clear();
 149 
 150             // change interest set for writing, channel should be selected
 151             key.interestOps(SelectionKey.OP_WRITE);
 152             n = sel.select();
 153             assertTrue(n == 1);
 154             assertTrue(sel.selectedKeys().contains(key));
 155             assertTrue(key.isWritable());
 156             assertFalse(key.isReadable());
 157             sel.selectedKeys().clear();
 158 
 159             // change interest set for reading + writing, channel should be selected
 160             key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
 161             n = sel.select();
 162             assertTrue(n == 1);
 163             assertTrue(sel.selectedKeys().contains(key));
 164             assertTrue(key.isWritable());
 165             assertTrue(key.isReadable());
 166             sel.selectedKeys().clear();
 167 
 168             // change interest set to 0 to deregister, channel should not be selected
 169             key.interestOps(0);
 170             n = sel.selectNow();
 171             assertTrue(n == 0);
 172 
 173         } finally {
 174             sel.close();
 175         }
 176     }
 177 
 178     /**
 179      * Test that the SelectableChannelCloser implCloseChannel method is invoked.
 180      */
 181     public void testImplCloseChannel() throws IOException {
 182         try (Connection connection = Connection.open()) {
 183             FileDescriptor fd = getFD(connection.channel1());
 184             Closer closer = new Closer();
 185             SelectableChannel ch = Channels.readWriteSelectableChannel(fd, closer);
 186 
 187             // close channel twice, checking that the closer is invoked only once
 188             for (int i=0; i<2; i++) {
 189                 ch.close();
 190 
 191                 // implCloseChannel should been invoked once
 192                 assertTrue(closer.closeCount == 1);
 193                 assertTrue(closer.invokedToClose == ch);
 194 
 195                 // implReleaseChannel should not have been invoked
 196                 assertTrue(closer.releaseCount == 0);
 197             }
 198         }
 199     }
 200 
 201     /**
 202      * Test that the SelectableChannelCloser implReleaseChannel method is invoked.
 203      */
 204     public void testImplReleaseChannel() throws IOException {
 205         Selector sel = Selector.open();
 206         try (Connection connection = Connection.open()) {
 207             FileDescriptor fd = getFD(connection.channel1());
 208             Closer closer = new Closer();
 209             SelectableChannel ch = Channels.readWriteSelectableChannel(fd, closer);
 210 
 211             // register with Selector, invoking selectNow to ensure registered
 212             ch.configureBlocking(false);
 213             ch.register(sel, SelectionKey.OP_WRITE);
 214             sel.selectNow();
 215 
 216             // close channel
 217             ch.close();
 218 
 219             // implCloseChannel should have been invoked
 220             assertTrue(closer.closeCount == 1);
 221             assertTrue(closer.invokedToClose == ch);
 222 
 223             // implReleaseChannel should not have been invoked
 224             assertTrue(closer.releaseCount == 0);
 225 
 226             // flush the selector
 227             sel.selectNow();
 228 
 229             // implReleaseChannel should have been invoked
 230             assertTrue(closer.releaseCount == 1);
 231             assertTrue(closer.invokedToRelease == ch);
 232 
 233         } finally {
 234             sel.close();
 235         }
 236     }
 237 
 238     @Test(expectedExceptions = IllegalArgumentException.class)
 239     public void testInvalidFileDescriptor() throws IOException {
 240         FileDescriptor fd = IOUtil.newFD(-1);
 241         Channels.readWriteSelectableChannel(fd, new SelectableChannelCloser() {
 242             @Override
 243             public void implCloseChannel(SelectableChannel sc) { }
 244             @Override
 245             public void implReleaseChannel(SelectableChannel sc) { }
 246         });
 247     }
 248 
 249     @Test(expectedExceptions = NullPointerException.class)
 250     public void testNullFileDescriptor() throws IOException {
 251         Channels.readWriteSelectableChannel(null, new SelectableChannelCloser() {
 252             @Override
 253             public void implCloseChannel(SelectableChannel sc) { }
 254             @Override
 255             public void implReleaseChannel(SelectableChannel sc) { }
 256         });
 257     }
 258 
 259     @Test(expectedExceptions = NullPointerException.class)
 260     public void testNullCloser() throws IOException {
 261         try (Connection connection = Connection.open()) {
 262             FileDescriptor fd = getFD(connection.channel1());
 263             Channels.readWriteSelectableChannel(fd, null);
 264         }
 265     }
 266 
 267     private static FileDescriptor getFD(SocketChannel sc) {
 268         try {
 269             Class<?> clazz = sc.getClass();
 270             Field f = clazz.getDeclaredField("fd");
 271             f.setAccessible(true);
 272             return (FileDescriptor) f.get(sc);
 273         } catch (Exception e) {
 274             throw new Error(e);
 275         }
 276     }
 277 }