1 /*
   2  * Copyright (c) 2000, 2009, 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.io.FileDescriptor;
  29 import java.io.IOException;
  30 import java.net.*;
  31 import java.nio.channels.*;
  32 import java.nio.channels.spi.*;
  33 import java.util.*;
  34 import sun.net.NetHooks;
  35 
  36 
  37 /**
  38  * An implementation of ServerSocketChannels
  39  */
  40 
  41 class ServerSocketChannelImpl
  42     extends ServerSocketChannel
  43     implements SelChImpl
  44 {
  45 
  46     // Used to make native close and configure calls
  47     private static NativeDispatcher nd;
  48 
  49     // Our file descriptor
  50     private final FileDescriptor fd;
  51 
  52     // fd value needed for dev/poll. This value will remain valid
  53     // even after the value in the file descriptor object has been set to -1
  54     private int fdVal;
  55 
  56     // ID of native thread currently blocked in this channel, for signalling
  57     private volatile long thread = 0;
  58 
  59     // Lock held by thread currently blocked in this channel
  60     private final Object lock = new Object();
  61 
  62     // Lock held by any thread that modifies the state fields declared below
  63     // DO NOT invoke a blocking I/O operation while holding this lock!
  64     private final Object stateLock = new Object();
  65 
  66     // -- The following fields are protected by stateLock
  67 
  68     // Channel state, increases monotonically
  69     private static final int ST_UNINITIALIZED = -1;
  70     private static final int ST_INUSE = 0;
  71     private static final int ST_KILLED = 1;
  72     private int state = ST_UNINITIALIZED;
  73 
  74     // Binding
  75     private SocketAddress localAddress; // null => unbound
  76 
  77     // Our socket adaptor, if any
  78     ServerSocket socket;
  79 
  80     // -- End of fields protected by stateLock
  81 
  82 
  83     ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
  84         super(sp);
  85         this.fd =  Net.serverSocket(true);
  86         this.fdVal = IOUtil.fdVal(fd);
  87         this.state = ST_INUSE;
  88     }
  89 
  90     ServerSocketChannelImpl(SelectorProvider sp,
  91                             FileDescriptor fd,
  92                             boolean bound)
  93         throws IOException
  94     {
  95         super(sp);
  96         this.fd =  fd;
  97         this.fdVal = IOUtil.fdVal(fd);
  98         this.state = ST_INUSE;
  99         if (bound)
 100             localAddress = Net.localAddress(fd);
 101     }
 102 
 103     public ServerSocket socket() {
 104         synchronized (stateLock) {
 105             if (socket == null)
 106                 socket = ServerSocketAdaptor.create(this);
 107             return socket;
 108         }
 109     }
 110 
 111     @Override
 112     public SocketAddress getLocalAddress() throws IOException {
 113         synchronized (stateLock) {
 114             if (!isOpen())
 115                 throw new ClosedChannelException();
 116             return localAddress;
 117         }
 118     }
 119 
 120     @Override
 121     public <T> ServerSocketChannel setOption(SocketOption<T> name, T value)
 122         throws IOException
 123     {
 124         if (name == null)
 125             throw new NullPointerException();
 126         if (!supportedOptions().contains(name))
 127             throw new UnsupportedOperationException("'" + name + "' not supported");
 128 
 129         synchronized (stateLock) {
 130             if (!isOpen())
 131                 throw new ClosedChannelException();
 132 
 133             // no options that require special handling
 134             Net.setSocketOption(fd, Net.UNSPEC, name, value);
 135             return this;
 136         }
 137     }
 138 
 139     @Override
 140     @SuppressWarnings("unchecked")
 141     public <T> T getOption(SocketOption<T> name)
 142         throws IOException
 143     {
 144         if (name == null)
 145             throw new NullPointerException();
 146         if (!supportedOptions().contains(name))
 147             throw new UnsupportedOperationException("'" + name + "' not supported");
 148 
 149         synchronized (stateLock) {
 150             if (!isOpen())
 151                 throw new ClosedChannelException();
 152 
 153             // no options that require special handling
 154             return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
 155         }
 156     }
 157 
 158     private static class DefaultOptionsHolder {
 159         static final Set<SocketOption<?>> defaultOptions = defaultOptions();
 160 
 161         private static Set<SocketOption<?>> defaultOptions() {
 162             HashSet<SocketOption<?>> set = new HashSet<SocketOption<?>>(2);
 163             set.add(StandardSocketOption.SO_RCVBUF);
 164             set.add(StandardSocketOption.SO_REUSEADDR);
 165             return Collections.unmodifiableSet(set);
 166         }
 167     }
 168 
 169     @Override
 170     public final Set<SocketOption<?>> supportedOptions() {
 171         return DefaultOptionsHolder.defaultOptions;
 172     }
 173 
 174     public boolean isBound() {
 175         synchronized (stateLock) {
 176             return localAddress != null;
 177         }
 178     }
 179 
 180     public SocketAddress localAddress() {
 181         synchronized (stateLock) {
 182             return localAddress;
 183         }
 184     }
 185 
 186     @Override
 187     public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {
 188         synchronized (lock) {
 189             if (!isOpen())
 190                 throw new ClosedChannelException();
 191             if (isBound())
 192                 throw new AlreadyBoundException();
 193             InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) :
 194                 Net.checkAddress(local);
 195             SecurityManager sm = System.getSecurityManager();
 196             if (sm != null)
 197                 sm.checkListen(isa.getPort());
 198             NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
 199             Net.bind(fd, isa.getAddress(), isa.getPort());
 200             Net.listen(fd, backlog < 1 ? 50 : backlog);
 201             synchronized (stateLock) {
 202                 localAddress = Net.localAddress(fd);
 203             }
 204         }
 205         return this;
 206     }
 207 
 208     public SocketChannel accept() throws IOException {
 209         synchronized (lock) {
 210             if (!isOpen())
 211                 throw new ClosedChannelException();
 212             if (!isBound())
 213                 throw new NotYetBoundException();
 214             SocketChannel sc = null;
 215 
 216             int n = 0;
 217             FileDescriptor newfd = new FileDescriptor();
 218             InetSocketAddress[] isaa = new InetSocketAddress[1];
 219 
 220             try {
 221                 begin();
 222                 if (!isOpen())
 223                     return null;
 224                 thread = NativeThread.current();
 225                 for (;;) {
 226                     n = accept0(this.fd, newfd, isaa);
 227                     if ((n == IOStatus.INTERRUPTED) && isOpen())
 228                         continue;
 229                     break;
 230                 }
 231             } finally {
 232                 thread = 0;
 233                 end(n > 0);
 234                 assert IOStatus.check(n);
 235             }
 236 
 237             if (n < 1)
 238                 return null;
 239 
 240             IOUtil.configureBlocking(newfd, true);
 241             InetSocketAddress isa = isaa[0];
 242             sc = new SocketChannelImpl(provider(), newfd, isa);
 243             SecurityManager sm = System.getSecurityManager();
 244             if (sm != null) {
 245                 try {
 246                     sm.checkAccept(isa.getAddress().getHostAddress(),
 247                                    isa.getPort());
 248                 } catch (SecurityException x) {
 249                     sc.close();
 250                     throw x;
 251                 }
 252             }
 253             return sc;
 254 
 255         }
 256     }
 257 
 258     protected void implConfigureBlocking(boolean block) throws IOException {
 259         IOUtil.configureBlocking(fd, block);
 260     }
 261 
 262     protected void implCloseSelectableChannel() throws IOException {
 263         synchronized (stateLock) {
 264             nd.preClose(fd);
 265             long th = thread;
 266             if (th != 0)
 267                 NativeThread.signal(th);
 268             if (!isRegistered())
 269                 kill();
 270         }
 271     }
 272 
 273     public void kill() throws IOException {
 274         synchronized (stateLock) {
 275             if (state == ST_KILLED)
 276                 return;
 277             if (state == ST_UNINITIALIZED) {
 278                 state = ST_KILLED;
 279                 return;
 280             }
 281             assert !isOpen() && !isRegistered();
 282             nd.close(fd);
 283             state = ST_KILLED;
 284         }
 285     }
 286 
 287     /**
 288      * Translates native poll revent set into a ready operation set
 289      */
 290     public boolean translateReadyOps(int ops, int initialOps,
 291                                      SelectionKeyImpl sk) {
 292         int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes
 293         int oldOps = sk.nioReadyOps();
 294         int newOps = initialOps;
 295 
 296         if ((ops & PollArrayWrapper.POLLNVAL) != 0) {
 297             // This should only happen if this channel is pre-closed while a
 298             // selection operation is in progress
 299             // ## Throw an error if this channel has not been pre-closed
 300             return false;
 301         }
 302 
 303         if ((ops & (PollArrayWrapper.POLLERR
 304                     | PollArrayWrapper.POLLHUP)) != 0) {
 305             newOps = intOps;
 306             sk.nioReadyOps(newOps);
 307             return (newOps & ~oldOps) != 0;
 308         }
 309 
 310         if (((ops & PollArrayWrapper.POLLIN) != 0) &&
 311             ((intOps & SelectionKey.OP_ACCEPT) != 0))
 312                 newOps |= SelectionKey.OP_ACCEPT;
 313 
 314         sk.nioReadyOps(newOps);
 315         return (newOps & ~oldOps) != 0;
 316     }
 317 
 318     public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
 319         return translateReadyOps(ops, sk.nioReadyOps(), sk);
 320     }
 321 
 322     public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
 323         return translateReadyOps(ops, 0, sk);
 324     }
 325 
 326     /**
 327      * Translates an interest operation set into a native poll event set
 328      */
 329     public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
 330         int newOps = 0;
 331 
 332         // Translate ops
 333         if ((ops & SelectionKey.OP_ACCEPT) != 0)
 334             newOps |= PollArrayWrapper.POLLIN;
 335         // Place ops into pollfd array
 336         sk.selector.putEventOps(sk, newOps);
 337     }
 338 
 339     public FileDescriptor getFD() {
 340         return fd;
 341     }
 342 
 343     public int getFDVal() {
 344         return fdVal;
 345     }
 346 
 347     public String toString() {
 348         StringBuffer sb = new StringBuffer();
 349         sb.append(this.getClass().getName());
 350         sb.append('[');
 351         if (!isOpen())
 352             sb.append("closed");
 353         else {
 354             synchronized (stateLock) {
 355                 if (localAddress() == null) {
 356                     sb.append("unbound");
 357                 } else {
 358                     sb.append(localAddress().toString());
 359                 }
 360             }
 361         }
 362         sb.append(']');
 363         return sb.toString();
 364     }
 365 
 366     // -- Native methods --
 367 
 368     // Accepts a new connection, setting the given file descriptor to refer to
 369     // the new socket and setting isaa[0] to the socket's remote address.
 370     // Returns 1 on success, or IOStatus.UNAVAILABLE (if non-blocking and no
 371     // connections are pending) or IOStatus.INTERRUPTED.
 372     //
 373     private native int accept0(FileDescriptor ssfd, FileDescriptor newfd,
 374                                InetSocketAddress[] isaa)
 375         throws IOException;
 376 
 377     private static native void initIDs();
 378 
 379     static {
 380         Util.load();
 381         initIDs();
 382         nd = new SocketDispatcher();
 383     }
 384 
 385 }