/* * Copyright (c) 2001, 2012, 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 * @summary Test socket-channel connection-state transitions * @library .. */ import java.io.*; import java.net.*; import java.nio.*; import java.nio.channels.*; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; public class ConnectState { static PrintStream log = System.err; static InetSocketAddress remote; final static int ST_UNCONNECTED = 0; final static int ST_PENDING = 1; final static int ST_CONNECTED = 2; final static int ST_CLOSED = 3; final static int ST_PENDING_OR_CONNECTED = 4; // NO exceptions expected final static Collection> NONE = Collections.emptySet(); // make a set of expected exception. // calling expectedExceptions(null) will always return NONE. static Collection> expectedExceptions(Class... expected) { final Collection> exceptions; if (expected == null || expected.length == 0) { exceptions = NONE; } else if (expected.length == 0 && expected[0] == null) { exceptions = NONE; } else if (expected.length == 1) { exceptions = Collections.>singleton(expected[0]); } else { exceptions = new HashSet<>(Arrays.asList(expected)); } return exceptions; } static abstract class Test { abstract String go(SocketChannel sc) throws Exception; static void check(boolean test, String desc) throws Exception { if (!test) throw new Exception("Incorrect state: " + desc); } static void check(SocketChannel sc, int state) throws Exception { switch (state) { case ST_UNCONNECTED: check(!sc.isConnected(), "!isConnected"); check(!sc.isConnectionPending(), "!isConnectionPending"); check(sc.isOpen(), "isOpen"); break; case ST_PENDING: check(!sc.isConnected(), "!isConnected"); check(sc.isConnectionPending(), "isConnectionPending"); check(sc.isOpen(), "isOpen"); break; case ST_CONNECTED: check(sc.isConnected(), "isConnected"); check(!sc.isConnectionPending(), "!isConnectionPending"); check(sc.isOpen(), "isOpen"); break; case ST_CLOSED: check(sc.isConnected(), "isConnected"); check(!sc.isConnectionPending(), "!isConnectionPending"); check(sc.isOpen(), "isOpen"); break; case ST_PENDING_OR_CONNECTED: check(sc.isConnected() || sc.isConnectionPending(), "isConnected || isConnectionPending"); check(sc.isOpen(), "isOpen"); break; } } Test(String name, Class exception, int state) throws Exception { this(name, expectedExceptions(exception), state); } // On some architecture we may need to accept several exceptions. // For instance on Solaris, when using a server colocated on the // machine we cannot guarantee that we will get a ConnectionPending // exception when connecting twice on the same non-blocking socket. // We may get a an AlreadyConnected exception, which is also valid: it // simply means that the first connection as been immediately accepted. Test(String name, Collection> exceptions, int state) throws Exception { SocketChannel sc = SocketChannel.open(); String note; try { try { note = go(sc); } catch (Exception x) { Class expectedExceptionClass = null; for (Class exception : exceptions) { if (exception.isInstance(x)) { log.println(name + ": As expected: " + x); expectedExceptionClass = exception; check(sc, state); break; } } if (expectedExceptionClass == null && !exceptions.isEmpty()) { // we had an exception, but it's not of the set of // exceptions we expected. throw new Exception(name + ": Incorrect exception", x); } else if (exceptions.isEmpty()) { // we didn't expect any exception throw new Exception(name + ": Unexpected exception", x); } // if we reach here, we have our expected exception assert expectedExceptionClass != null; return; } if (!exceptions.isEmpty()) { throw new Exception(name + ": Expected exception not thrown: " + exceptions.iterator().next()); } check(sc, state); log.println(name + ": Returned normally" + ((note != null) ? ": " + note : "")); } finally { if (sc.isOpen()) sc.close(); } } } static void tests() throws Exception { log.println(remote); new Test("Read unconnected", NotYetConnectedException.class, ST_UNCONNECTED) { @Override String go(SocketChannel sc) throws Exception { ByteBuffer b = ByteBuffer.allocateDirect(1024); sc.read(b); return null; }}; new Test("Write unconnected", NotYetConnectedException.class, ST_UNCONNECTED) { @Override String go(SocketChannel sc) throws Exception { ByteBuffer b = ByteBuffer.allocateDirect(1024); sc.write(b); return null; }}; new Test("Simple connect", NONE, ST_CONNECTED) { @Override String go(SocketChannel sc) throws Exception { sc.connect(remote); return null; }}; new Test("Simple connect & finish", NONE, ST_CONNECTED) { @Override String go(SocketChannel sc) throws Exception { sc.connect(remote); if (!sc.finishConnect()) throw new Exception("finishConnect returned false"); return null; }}; new Test("Double connect", AlreadyConnectedException.class, ST_CONNECTED) { @Override String go(SocketChannel sc) throws Exception { sc.connect(remote); sc.connect(remote); return null; }}; new Test("Finish w/o start", NoConnectionPendingException.class, ST_UNCONNECTED) { @Override String go(SocketChannel sc) throws Exception { sc.finishConnect(); return null; }}; // Note: using our local EchoServer rather than echo on a distant // host - we see that Tries to finish = 0 (instead of ~ 18). new Test("NB simple connect", NONE, ST_CONNECTED) { @Override String go(SocketChannel sc) throws Exception { sc.configureBlocking(false); sc.connect(remote); int n = 0; while (!sc.finishConnect()) { Thread.sleep(10); n++; } sc.finishConnect(); // Check redundant invocation return ("Tries to finish = " + n); }}; // Note: using our local EchoServer rather than echo on a distant // host - we cannot guarantee that this test will get a // a ConnectionPendingException: it may get an AlreadyConnectedException, // so we should allow for both. new Test("NB double connect", expectedExceptions( ConnectionPendingException.class, AlreadyConnectedException.class), ST_PENDING_OR_CONNECTED) { @Override String go(SocketChannel sc) throws Exception { sc.configureBlocking(false); sc.connect(remote); sc.connect(remote); return null; }}; new Test("NB finish w/o start", NoConnectionPendingException.class, ST_UNCONNECTED) { @Override String go(SocketChannel sc) throws Exception { sc.configureBlocking(false); sc.finishConnect(); return null; }}; new Test("NB connect, B finish", NONE, ST_CONNECTED) { @Override String go(SocketChannel sc) throws Exception { sc.configureBlocking(false); sc.connect(remote); sc.configureBlocking(true); sc.finishConnect(); return null; }}; } public static void main(String[] args) throws Exception { try (TestUtil.EchoServer echoServer = TestUtil.EchoServer.startNewServer(500)) { remote = new InetSocketAddress(echoServer.getAddress(), echoServer.getPort()); tests(); } } }