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 
  26 package sun.nio.ch;
  27 
  28 import java.nio.channels.*;
  29 import java.net.SocketAddress;
  30 import java.net.SocketOption;
  31 import java.net.StandardSocketOptions;
  32 import java.net.InetSocketAddress;
  33 import java.io.FileDescriptor;
  34 import java.io.IOException;
  35 import java.util.Set;
  36 import java.util.HashSet;
  37 import java.util.Collections;
  38 import java.util.concurrent.Future;
  39 import java.util.concurrent.locks.ReadWriteLock;
  40 import java.util.concurrent.locks.ReentrantReadWriteLock;
  41 import sun.net.NetHooks;
  42 
  43 /**
  44  * Base implementation of AsynchronousServerSocketChannel.
  45  */
  46 
  47 abstract 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<>(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 }