1/*
2 * Copyright (c) 2008, 2013, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.nio.ch;
27
28import java.nio.channels.*;
29import java.net.SocketAddress;
30import java.net.SocketOption;
31import java.net.StandardSocketOptions;
32import java.net.InetSocketAddress;
33import java.io.FileDescriptor;
34import java.io.IOException;
35import java.util.Set;
36import java.util.HashSet;
37import java.util.Collections;
38import java.util.concurrent.Future;
39import java.util.concurrent.locks.ReadWriteLock;
40import java.util.concurrent.locks.ReentrantReadWriteLock;
41import sun.net.NetHooks;
42
43/**
44 * Base implementation of AsynchronousServerSocketChannel.
45 */
46
47abstract class AsynchronousServerSocketChannelImpl
48    extends AsynchronousServerSocketChannel
49    implements Cancellable, Groupable
50{
51    protected final FileDescriptor fd;
52
53    // the local address to which the channel's socket is bound
54    protected volatile InetSocketAddress localAddress = null;
55
56    // need this lock to set local address
57    private final Object stateLock = new Object();
58
59    // close support
60    private ReadWriteLock closeLock = new ReentrantReadWriteLock();
61    private volatile boolean open = true;
62
63    // set true when accept operation is cancelled
64    private volatile boolean acceptKilled;
65
66    // set true when exclusive binding is on and SO_REUSEADDR is emulated
67    private boolean isReuseAddress;
68
69    AsynchronousServerSocketChannelImpl(AsynchronousChannelGroupImpl group) {
70        super(group.provider());
71        this.fd = Net.serverSocket(true);
72    }
73
74    @Override
75    public final boolean isOpen() {
76        return open;
77    }
78
79    /**
80     * Marks beginning of access to file descriptor/handle
81     */
82    final void begin() throws IOException {
83        closeLock.readLock().lock();
84        if (!isOpen())
85            throw new ClosedChannelException();
86    }
87
88    /**
89     * Marks end of access to file descriptor/handle
90     */
91    final void end() {
92        closeLock.readLock().unlock();
93    }
94
95    /**
96     * Invoked to close file descriptor/handle.
97     */
98    abstract void implClose() throws IOException;
99
100    @Override
101    public final void close() throws IOException {
102        // synchronize with any threads using file descriptor/handle
103        closeLock.writeLock().lock();
104        try {
105            if (!open)
106                return;     // already closed
107            open = false;
108        } finally {
109            closeLock.writeLock().unlock();
110        }
111        implClose();
112    }
113
114    /**
115     * Invoked by accept to accept connection
116     */
117    abstract Future<AsynchronousSocketChannel>
118        implAccept(Object attachment,
119                   CompletionHandler<AsynchronousSocketChannel,Object> handler);
120
121
122    @Override
123    public final Future<AsynchronousSocketChannel> accept() {
124        return implAccept(null, null);
125    }
126
127    @Override
128    @SuppressWarnings("unchecked")
129    public final <A> void accept(A attachment,
130                                 CompletionHandler<AsynchronousSocketChannel,? super A> handler)
131    {
132        if (handler == null)
133            throw new NullPointerException("'handler' is null");
134        implAccept(attachment, (CompletionHandler<AsynchronousSocketChannel,Object>)handler);
135    }
136
137    final boolean isAcceptKilled() {
138        return acceptKilled;
139    }
140
141    @Override
142    public final void onCancel(PendingFuture<?,?> task) {
143        acceptKilled = true;
144    }
145
146    @Override
147    public final AsynchronousServerSocketChannel bind(SocketAddress local, int backlog)
148        throws IOException
149    {
150        InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) :
151            Net.checkAddress(local);
152        SecurityManager sm = System.getSecurityManager();
153        if (sm != null)
154            sm.checkListen(isa.getPort());
155
156        try {
157            begin();
158            synchronized (stateLock) {
159                if (localAddress != null)
160                    throw new AlreadyBoundException();
161                NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
162                Net.bind(fd, isa.getAddress(), isa.getPort());
163                Net.listen(fd, backlog < 1 ? 50 : backlog);
164                localAddress = Net.localAddress(fd);
165            }
166        } finally {
167            end();
168        }
169        return this;
170    }
171
172    @Override
173    public final SocketAddress getLocalAddress() throws IOException {
174        if (!isOpen())
175            throw new ClosedChannelException();
176        return Net.getRevealedLocalAddress(localAddress);
177    }
178
179    @Override
180    public final <T> AsynchronousServerSocketChannel setOption(SocketOption<T> name,
181                                                               T value)
182        throws IOException
183    {
184        if (name == null)
185            throw new NullPointerException();
186        if (!supportedOptions().contains(name))
187            throw new UnsupportedOperationException("'" + name + "' not supported");
188
189        try {
190            begin();
191            if (name == StandardSocketOptions.SO_REUSEADDR &&
192                    Net.useExclusiveBind())
193            {
194                // SO_REUSEADDR emulated when using exclusive bind
195                isReuseAddress = (Boolean)value;
196            } else {
197                Net.setSocketOption(fd, Net.UNSPEC, name, value);
198            }
199            return this;
200        } finally {
201            end();
202        }
203    }
204
205    @Override
206    @SuppressWarnings("unchecked")
207    public final <T> T getOption(SocketOption<T> name) throws IOException {
208        if (name == null)
209            throw new NullPointerException();
210        if (!supportedOptions().contains(name))
211            throw new UnsupportedOperationException("'" + name + "' not supported");
212
213        try {
214            begin();
215            if (name == StandardSocketOptions.SO_REUSEADDR &&
216                    Net.useExclusiveBind())
217            {
218                // SO_REUSEADDR emulated when using exclusive bind
219                return (T)Boolean.valueOf(isReuseAddress);
220            }
221            return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
222        } finally {
223            end();
224        }
225    }
226
227    private static class DefaultOptionsHolder {
228        static final Set<SocketOption<?>> defaultOptions = defaultOptions();
229
230        private static Set<SocketOption<?>> defaultOptions() {
231            HashSet<SocketOption<?>> set = new HashSet<SocketOption<?>>(2);
232            set.add(StandardSocketOptions.SO_RCVBUF);
233            set.add(StandardSocketOptions.SO_REUSEADDR);
234            return Collections.unmodifiableSet(set);
235        }
236    }
237
238    @Override
239    public final Set<SocketOption<?>> supportedOptions() {
240        return DefaultOptionsHolder.defaultOptions;
241    }
242
243    @Override
244    public final String toString() {
245        StringBuilder sb = new StringBuilder();
246        sb.append(this.getClass().getName());
247        sb.append('[');
248        if (!isOpen())
249            sb.append("closed");
250        else {
251            if (localAddress == null) {
252                sb.append("unbound");
253            } else {
254                sb.append(Net.getRevealedLocalAddressAsString(localAddress));
255            }
256        }
257        sb.append(']');
258        return sb.toString();
259    }
260}
261