Home | History | Annotate | Download | only in ch
      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<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