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 }