1 /* 2 * Copyright (c) 2001, 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 /* @test 25 * @summary Test socket-channel connection-state transitions 26 * @library .. /test/lib 27 * @build jdk.test.lib.Utils TestServers 28 * @run main ConnectState 29 */ 30 31 import java.io.*; 32 import java.net.*; 33 import java.nio.*; 34 import java.nio.channels.*; 35 import java.util.Arrays; 36 import java.util.Collection; 37 import java.util.Collections; 38 import java.util.HashSet; 39 40 41 public class ConnectState { 42 43 static PrintStream log = System.err; 44 45 static InetSocketAddress remote; 46 47 final static int ST_UNCONNECTED = 0; 48 final static int ST_PENDING = 1; 49 final static int ST_CONNECTED = 2; 50 final static int ST_CLOSED = 3; 51 final static int ST_PENDING_OR_CONNECTED = 4; 52 // NO exceptions expected 53 final static Collection<Class<?>> NONE = Collections.emptySet(); 54 55 // make a set of expected exception. expectedExceptions(Class<?>.... expected)56 static Collection<Class<?>> expectedExceptions(Class<?>... expected) { 57 final Collection<Class<?>> exceptions; 58 if (expected.length == 0) { 59 exceptions = NONE; 60 } else if (expected.length == 1) { 61 assert expected[0] != null; 62 exceptions = Collections.<Class<?>>singleton(expected[0]); 63 } else { 64 exceptions = new HashSet<>(Arrays.asList(expected)); 65 } 66 return exceptions; 67 } 68 69 static abstract class Test { 70 go(SocketChannel sc)71 abstract String go(SocketChannel sc) throws Exception; 72 check(boolean test, String desc)73 static void check(boolean test, String desc) throws Exception { 74 if (!test) 75 throw new Exception("Incorrect state: " + desc); 76 } 77 check(SocketChannel sc, int state)78 static void check(SocketChannel sc, int state) throws Exception { 79 switch (state) { 80 case ST_UNCONNECTED: 81 check(!sc.isConnected(), "!isConnected"); 82 check(!sc.isConnectionPending(), "!isConnectionPending"); 83 check(sc.isOpen(), "isOpen"); 84 break; 85 case ST_PENDING: 86 check(!sc.isConnected(), "!isConnected"); 87 check(sc.isConnectionPending(), "isConnectionPending"); 88 check(sc.isOpen(), "isOpen"); 89 break; 90 case ST_CONNECTED: 91 check(sc.isConnected(), "isConnected"); 92 check(!sc.isConnectionPending(), "!isConnectionPending"); 93 check(sc.isOpen(), "isOpen"); 94 break; 95 case ST_CLOSED: 96 check(sc.isConnected(), "isConnected"); 97 check(!sc.isConnectionPending(), "!isConnectionPending"); 98 check(sc.isOpen(), "isOpen"); 99 break; 100 case ST_PENDING_OR_CONNECTED: 101 check(sc.isConnected() || sc.isConnectionPending(), 102 "isConnected || isConnectionPending"); 103 check(sc.isOpen(), "isOpen"); 104 break; 105 } 106 } 107 Test(String name, Class<?> exception, int state)108 Test(String name, Class<?> exception, int state) throws Exception { 109 this(name, expectedExceptions(exception), state); 110 } 111 112 // On some architecture we may need to accept several exceptions. 113 // For instance on Solaris, when using a server colocated on the 114 // machine we cannot guarantee that we will get a 115 // ConnectionPendingException when connecting twice on the same 116 // non-blocking socket. We may instead get a an 117 // AlreadyConnectedException, which is also valid: it simply means 118 // that the first connection has been immediately accepted. Test(String name, Collection<Class<?>> exceptions, int state)119 Test(String name, Collection<Class<?>> exceptions, int state) 120 throws Exception { 121 SocketChannel sc = SocketChannel.open(); 122 String note; 123 try { 124 try { 125 note = go(sc); 126 } catch (Exception x) { 127 Class<?> expectedExceptionClass = null; 128 for (Class<?> exception : exceptions) { 129 if (exception.isInstance(x)) { 130 log.println(name + ": As expected: " 131 + x); 132 expectedExceptionClass = exception; 133 check(sc, state); 134 break; 135 } 136 } 137 if (expectedExceptionClass == null 138 && !exceptions.isEmpty()) { 139 // we had an exception, but it's not of the set of 140 // exceptions we expected. 141 throw new Exception(name 142 + ": Incorrect exception", 143 x); 144 } else if (exceptions.isEmpty()) { 145 // we didn't expect any exception 146 throw new Exception(name 147 + ": Unexpected exception", 148 x); 149 } 150 // if we reach here, we have our expected exception 151 assert expectedExceptionClass != null; 152 return; 153 } 154 if (!exceptions.isEmpty()) { 155 throw new Exception(name 156 + ": Expected exception not thrown: " 157 + exceptions.iterator().next()); 158 } 159 check(sc, state); 160 log.println(name + ": Returned normally" 161 + ((note != null) ? ": " + note : "")); 162 } finally { 163 if (sc.isOpen()) 164 sc.close(); 165 } 166 } 167 168 } 169 tests()170 static void tests() throws Exception { 171 log.println(remote); 172 173 new Test("Read unconnected", NotYetConnectedException.class, 174 ST_UNCONNECTED) { 175 @Override 176 String go(SocketChannel sc) throws Exception { 177 ByteBuffer b = ByteBuffer.allocateDirect(1024); 178 sc.read(b); 179 return null; 180 }}; 181 182 new Test("Write unconnected", NotYetConnectedException.class, 183 ST_UNCONNECTED) { 184 @Override 185 String go(SocketChannel sc) throws Exception { 186 ByteBuffer b = ByteBuffer.allocateDirect(1024); 187 sc.write(b); 188 return null; 189 }}; 190 191 new Test("Simple connect", NONE, ST_CONNECTED) { 192 @Override 193 String go(SocketChannel sc) throws Exception { 194 sc.connect(remote); 195 return null; 196 }}; 197 198 new Test("Simple connect & finish", NONE, ST_CONNECTED) { 199 @Override 200 String go(SocketChannel sc) throws Exception { 201 sc.connect(remote); 202 if (!sc.finishConnect()) 203 throw new Exception("finishConnect returned false"); 204 return null; 205 }}; 206 207 new Test("Double connect", 208 AlreadyConnectedException.class, ST_CONNECTED) { 209 @Override 210 String go(SocketChannel sc) throws Exception { 211 sc.connect(remote); 212 sc.connect(remote); 213 return null; 214 }}; 215 216 new Test("Finish w/o start", 217 NoConnectionPendingException.class, ST_UNCONNECTED) { 218 @Override 219 String go(SocketChannel sc) throws Exception { 220 sc.finishConnect(); 221 return null; 222 }}; 223 224 // Note: using our local EchoServer rather than echo on a distant 225 // host - we see that Tries to finish = 0 (instead of ~ 18). 226 new Test("NB simple connect", NONE, ST_CONNECTED) { 227 @Override 228 String go(SocketChannel sc) throws Exception { 229 sc.configureBlocking(false); 230 sc.connect(remote); 231 int n = 0; 232 while (!sc.finishConnect()) { 233 Thread.sleep(10); 234 n++; 235 } 236 sc.finishConnect(); // Check redundant invocation 237 return ("Tries to finish = " + n); 238 }}; 239 240 // Note: using our local EchoServer rather than echo on a distant 241 // host - we cannot guarantee that this test will get a 242 // a ConnectionPendingException: it may get an 243 // AlreadyConnectedException, so we should allow for both. 244 new Test("NB double connect", 245 expectedExceptions(ConnectionPendingException.class, 246 AlreadyConnectedException.class), 247 ST_PENDING_OR_CONNECTED) { 248 @Override 249 String go(SocketChannel sc) throws Exception { 250 sc.configureBlocking(false); 251 sc.connect(remote); 252 sc.connect(remote); 253 return null; 254 }}; 255 256 new Test("NB finish w/o start", 257 NoConnectionPendingException.class, ST_UNCONNECTED) { 258 @Override 259 String go(SocketChannel sc) throws Exception { 260 sc.configureBlocking(false); 261 sc.finishConnect(); 262 return null; 263 }}; 264 265 new Test("NB connect, B finish", NONE, ST_CONNECTED) { 266 @Override 267 String go(SocketChannel sc) throws Exception { 268 sc.configureBlocking(false); 269 sc.connect(remote); 270 sc.configureBlocking(true); 271 sc.finishConnect(); 272 return null; 273 }}; 274 275 } 276 main(String[] args)277 public static void main(String[] args) throws Exception { 278 try (TestServers.EchoServer echoServer 279 = TestServers.EchoServer.startNewServer(500)) { 280 remote = new InetSocketAddress(echoServer.getAddress(), 281 echoServer.getPort()); 282 tests(); 283 } 284 } 285 286 } 287