1/*
2 * Copyright (c) 2008, 2016, 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 * @bug 4607272 6842687
26 * @summary Unit test for AsynchronousServerSocketChannel
27 * @run main/timeout=180 Basic
28 */
29
30import java.nio.channels.*;
31import java.net.*;
32import static java.net.StandardSocketOptions.*;
33import java.io.IOException;
34import java.util.Set;
35import java.util.concurrent.ExecutionException;
36import java.util.concurrent.Future;
37import java.util.concurrent.atomic.AtomicReference;
38
39public class Basic {
40
41    public static void main(String[] args) throws Exception {
42        testBind();
43        testAccept();
44        testSocketOptions();
45    }
46
47    static void testBind() throws Exception {
48        System.out.println("-- bind --");
49
50        AsynchronousServerSocketChannel ch = AsynchronousServerSocketChannel.open();
51        if (ch.getLocalAddress() != null)
52            throw new RuntimeException("Local address should be 'null'");
53        ch.bind(new InetSocketAddress(0), 20);
54
55        // check local address after binding
56        InetSocketAddress local = (InetSocketAddress)ch.getLocalAddress();
57        if (local.getPort() == 0)
58            throw new RuntimeException("Unexpected port");
59        if (!local.getAddress().isAnyLocalAddress())
60            throw new RuntimeException("Not bound to a wildcard address");
61
62        // try to re-bind
63        try {
64            ch.bind(new InetSocketAddress(0));
65            throw new RuntimeException("AlreadyBoundException expected");
66        } catch (AlreadyBoundException x) {
67        }
68        ch.close();
69
70        // check ClosedChannelException
71        ch = AsynchronousServerSocketChannel.open();
72        ch.close();
73        try {
74            ch.bind(new InetSocketAddress(0));
75            throw new RuntimeException("ClosedChannelException  expected");
76        } catch (ClosedChannelException  x) {
77        }
78    }
79
80    static void testAccept() throws Exception {
81        System.out.println("-- accept --");
82
83        final AsynchronousServerSocketChannel listener =
84            AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0));
85
86        InetAddress lh = InetAddress.getLocalHost();
87        int port = ((InetSocketAddress)(listener.getLocalAddress())).getPort();
88        final InetSocketAddress isa = new InetSocketAddress(lh, port);
89
90        // establish a few loopback connections
91        for (int i=0; i<100; i++) {
92            SocketChannel sc = SocketChannel.open(isa);
93            AsynchronousSocketChannel ch = listener.accept().get();
94            sc.close();
95            ch.close();
96        }
97
98       final AtomicReference<Throwable> exception = new AtomicReference<Throwable>();
99
100        // start accepting
101        listener.accept((Void)null, new CompletionHandler<AsynchronousSocketChannel,Void>() {
102            public void completed(AsynchronousSocketChannel ch, Void att) {
103                try {
104                    ch.close();
105                } catch (IOException ignore) { }
106            }
107            public void failed(Throwable exc, Void att) {
108                exception.set(exc);
109            }
110        });
111
112        // check AcceptPendingException
113        try {
114            listener.accept();
115            throw new RuntimeException("AcceptPendingException expected");
116        } catch (AcceptPendingException x) {
117        }
118
119        // asynchronous close
120        listener.close();
121        while (exception.get() == null)
122            Thread.sleep(100);
123        if (!(exception.get() instanceof AsynchronousCloseException))
124            throw new RuntimeException("AsynchronousCloseException expected");
125
126        // once closed when a further attemt should throw ClosedChannelException
127        try {
128            listener.accept().get();
129            throw new RuntimeException("ExecutionException expected");
130        } catch (ExecutionException x) {
131            if (!(x.getCause() instanceof ClosedChannelException))
132                throw new RuntimeException("Cause of ClosedChannelException expected");
133        } catch (InterruptedException x) {
134        }
135
136    }
137
138    static void testSocketOptions() throws Exception {
139        System.out.println("-- socket options --");
140        AsynchronousServerSocketChannel ch = AsynchronousServerSocketChannel.open();
141        try {
142            // check supported options
143            Set<SocketOption<?>> options = ch.supportedOptions();
144            boolean reuseport = options.contains(SO_REUSEPORT);
145            if (!options.contains(SO_REUSEADDR))
146                throw new RuntimeException("SO_REUSEADDR should be supported");
147            if (!options.contains(SO_REUSEPORT) && reuseport)
148                throw new RuntimeException("SO_REUSEPORT should be supported");
149            if (!options.contains(SO_RCVBUF))
150                throw new RuntimeException("SO_RCVBUF should be supported");
151
152            // allowed to change when not bound
153            ch.setOption(SO_RCVBUF, 256*1024);     // can't check
154            int before = ch.getOption(SO_RCVBUF);
155            int after = ch.setOption(SO_RCVBUF, Integer.MAX_VALUE).getOption(SO_RCVBUF);
156            if (after < before)
157                 throw new RuntimeException("setOption caused SO_RCVBUF to decrease");
158            ch.setOption(SO_REUSEADDR, true);
159            checkOption(ch, SO_REUSEADDR, true);
160            ch.setOption(SO_REUSEADDR, false);
161            checkOption(ch, SO_REUSEADDR, false);
162
163            if (reuseport) {
164                ch.setOption(SO_REUSEPORT, true);
165                checkOption(ch, SO_REUSEPORT, true);
166                ch.setOption(SO_REUSEPORT, false);
167                checkOption(ch, SO_REUSEPORT, false);
168            }
169        } finally {
170            ch.close();
171        }
172    }
173
174    static void checkOption(AsynchronousServerSocketChannel ch,
175                            SocketOption name, Object expectedValue)
176        throws IOException
177    {
178        Object value = ch.getOption(name);
179        if (!value.equals(expectedValue))
180            throw new RuntimeException("value not as expected");
181    }
182}
183