| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.nio.ch;  | 
 | 
 | 
 | 
import java.io.FileDescriptor;  | 
 | 
import java.io.IOException;  | 
 | 
import java.net.*;  | 
 | 
import java.nio.channels.*;  | 
 | 
import java.nio.channels.spi.*;  | 
 | 
import java.util.*;  | 
 | 
import sun.net.NetHooks;  | 
 | 
import sun.net.ExtendedOptionsHelper;  | 
 | 
 | 
 | 
 | 
 | 
/**  | 
 | 
 * An implementation of ServerSocketChannels  | 
 | 
 */  | 
 | 
 | 
 | 
class ServerSocketChannelImpl  | 
 | 
    extends ServerSocketChannel  | 
 | 
    implements SelChImpl  | 
 | 
{ | 
 | 
 | 
 | 
      | 
 | 
    private static NativeDispatcher nd;  | 
 | 
 | 
 | 
      | 
 | 
    private final FileDescriptor fd;  | 
 | 
    private final int fdVal;  | 
 | 
 | 
 | 
      | 
 | 
    private volatile long thread = 0;  | 
 | 
 | 
 | 
      | 
 | 
    private final Object lock = new Object();  | 
 | 
 | 
 | 
    // Lock held by any thread that modifies the state fields declared below  | 
 | 
      | 
 | 
    private final Object stateLock = new Object();  | 
 | 
 | 
 | 
    // -- The following fields are protected by stateLock  | 
 | 
 | 
 | 
      | 
 | 
    private static final int ST_UNINITIALIZED = -1;  | 
 | 
    private static final int ST_INUSE = 0;  | 
 | 
    private static final int ST_KILLED = 1;  | 
 | 
    private int state = ST_UNINITIALIZED;  | 
 | 
 | 
 | 
    // Binding  | 
 | 
    private InetSocketAddress localAddress;   | 
 | 
 | 
 | 
      | 
 | 
    private boolean isReuseAddress;  | 
 | 
 | 
 | 
      | 
 | 
    ServerSocket socket;  | 
 | 
 | 
 | 
    // -- End of fields protected by stateLock  | 
 | 
 | 
 | 
 | 
 | 
    ServerSocketChannelImpl(SelectorProvider sp) throws IOException { | 
 | 
        super(sp);  | 
 | 
        this.fd =  Net.serverSocket(true);  | 
 | 
        this.fdVal = IOUtil.fdVal(fd);  | 
 | 
        this.state = ST_INUSE;  | 
 | 
    }  | 
 | 
 | 
 | 
    ServerSocketChannelImpl(SelectorProvider sp,  | 
 | 
                            FileDescriptor fd,  | 
 | 
                            boolean bound)  | 
 | 
        throws IOException  | 
 | 
    { | 
 | 
        super(sp);  | 
 | 
        this.fd =  fd;  | 
 | 
        this.fdVal = IOUtil.fdVal(fd);  | 
 | 
        this.state = ST_INUSE;  | 
 | 
        if (bound)  | 
 | 
            localAddress = Net.localAddress(fd);  | 
 | 
    }  | 
 | 
 | 
 | 
    public ServerSocket socket() { | 
 | 
        synchronized (stateLock) { | 
 | 
            if (socket == null)  | 
 | 
                socket = ServerSocketAdaptor.create(this);  | 
 | 
            return socket;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public SocketAddress getLocalAddress() throws IOException { | 
 | 
        synchronized (stateLock) { | 
 | 
            if (!isOpen())  | 
 | 
                throw new ClosedChannelException();  | 
 | 
            return localAddress == null ? localAddress  | 
 | 
                    : Net.getRevealedLocalAddress(  | 
 | 
                          Net.asInetSocketAddress(localAddress));  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public <T> ServerSocketChannel setOption(SocketOption<T> name, T value)  | 
 | 
        throws IOException  | 
 | 
    { | 
 | 
        if (name == null)  | 
 | 
            throw new NullPointerException();  | 
 | 
        if (!supportedOptions().contains(name))  | 
 | 
            throw new UnsupportedOperationException("'" + name + "' not supported"); | 
 | 
        synchronized (stateLock) { | 
 | 
            if (!isOpen())  | 
 | 
                throw new ClosedChannelException();  | 
 | 
 | 
 | 
            if (name == StandardSocketOptions.IP_TOS) { | 
 | 
                ProtocolFamily family = Net.isIPv6Available() ?  | 
 | 
                    StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;  | 
 | 
                Net.setSocketOption(fd, family, name, value);  | 
 | 
                return this;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (name == StandardSocketOptions.SO_REUSEADDR &&  | 
 | 
                    Net.useExclusiveBind())  | 
 | 
            { | 
 | 
                  | 
 | 
                isReuseAddress = (Boolean)value;  | 
 | 
            } else { | 
 | 
                  | 
 | 
                Net.setSocketOption(fd, Net.UNSPEC, name, value);  | 
 | 
            }  | 
 | 
            return this;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    @SuppressWarnings("unchecked") | 
 | 
    public <T> T getOption(SocketOption<T> name)  | 
 | 
        throws IOException  | 
 | 
    { | 
 | 
        if (name == null)  | 
 | 
            throw new NullPointerException();  | 
 | 
        if (!supportedOptions().contains(name))  | 
 | 
            throw new UnsupportedOperationException("'" + name + "' not supported"); | 
 | 
 | 
 | 
        synchronized (stateLock) { | 
 | 
            if (!isOpen())  | 
 | 
                throw new ClosedChannelException();  | 
 | 
            if (name == StandardSocketOptions.SO_REUSEADDR &&  | 
 | 
                    Net.useExclusiveBind())  | 
 | 
            { | 
 | 
                  | 
 | 
                return (T)Boolean.valueOf(isReuseAddress);  | 
 | 
            }  | 
 | 
              | 
 | 
            return (T) Net.getSocketOption(fd, Net.UNSPEC, name);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static class DefaultOptionsHolder { | 
 | 
        static final Set<SocketOption<?>> defaultOptions = defaultOptions();  | 
 | 
 | 
 | 
        private static Set<SocketOption<?>> defaultOptions() { | 
 | 
            HashSet<SocketOption<?>> set = new HashSet<SocketOption<?>>(2);  | 
 | 
            set.add(StandardSocketOptions.SO_RCVBUF);  | 
 | 
            set.add(StandardSocketOptions.SO_REUSEADDR);  | 
 | 
            set.add(StandardSocketOptions.IP_TOS);  | 
 | 
            set.addAll(ExtendedOptionsHelper.keepAliveOptions());  | 
 | 
            return Collections.unmodifiableSet(set);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public final Set<SocketOption<?>> supportedOptions() { | 
 | 
        return DefaultOptionsHolder.defaultOptions;  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean isBound() { | 
 | 
        synchronized (stateLock) { | 
 | 
            return localAddress != null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public InetSocketAddress localAddress() { | 
 | 
        synchronized (stateLock) { | 
 | 
            return localAddress;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException { | 
 | 
        synchronized (lock) { | 
 | 
            if (!isOpen())  | 
 | 
                throw new ClosedChannelException();  | 
 | 
            if (isBound())  | 
 | 
                throw new AlreadyBoundException();  | 
 | 
            InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) :  | 
 | 
                Net.checkAddress(local);  | 
 | 
            SecurityManager sm = System.getSecurityManager();  | 
 | 
            if (sm != null)  | 
 | 
                sm.checkListen(isa.getPort());  | 
 | 
            NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());  | 
 | 
            Net.bind(fd, isa.getAddress(), isa.getPort());  | 
 | 
            Net.listen(fd, backlog < 1 ? 50 : backlog);  | 
 | 
            synchronized (stateLock) { | 
 | 
                localAddress = Net.localAddress(fd);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return this;  | 
 | 
    }  | 
 | 
 | 
 | 
    public SocketChannel accept() throws IOException { | 
 | 
        synchronized (lock) { | 
 | 
            if (!isOpen())  | 
 | 
                throw new ClosedChannelException();  | 
 | 
            if (!isBound())  | 
 | 
                throw new NotYetBoundException();  | 
 | 
            SocketChannel sc = null;  | 
 | 
 | 
 | 
            int n = 0;  | 
 | 
            FileDescriptor newfd = new FileDescriptor();  | 
 | 
            InetSocketAddress[] isaa = new InetSocketAddress[1];  | 
 | 
 | 
 | 
            try { | 
 | 
                begin();  | 
 | 
                if (!isOpen())  | 
 | 
                    return null;  | 
 | 
                thread = NativeThread.current();  | 
 | 
                for (;;) { | 
 | 
                    n = accept(this.fd, newfd, isaa);  | 
 | 
                    if ((n == IOStatus.INTERRUPTED) && isOpen())  | 
 | 
                        continue;  | 
 | 
                    break;  | 
 | 
                }  | 
 | 
            } finally { | 
 | 
                thread = 0;  | 
 | 
                end(n > 0);  | 
 | 
                assert IOStatus.check(n);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (n < 1)  | 
 | 
                return null;  | 
 | 
 | 
 | 
            IOUtil.configureBlocking(newfd, true);  | 
 | 
            InetSocketAddress isa = isaa[0];  | 
 | 
            sc = new SocketChannelImpl(provider(), newfd, isa);  | 
 | 
            SecurityManager sm = System.getSecurityManager();  | 
 | 
            if (sm != null) { | 
 | 
                try { | 
 | 
                    sm.checkAccept(isa.getAddress().getHostAddress(),  | 
 | 
                                   isa.getPort());  | 
 | 
                } catch (SecurityException x) { | 
 | 
                    sc.close();  | 
 | 
                    throw x;  | 
 | 
                }  | 
 | 
            }  | 
 | 
            return sc;  | 
 | 
 | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    protected void implConfigureBlocking(boolean block) throws IOException { | 
 | 
        IOUtil.configureBlocking(fd, block);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected void implCloseSelectableChannel() throws IOException { | 
 | 
        synchronized (stateLock) { | 
 | 
            if (state != ST_KILLED)  | 
 | 
                nd.preClose(fd);  | 
 | 
            long th = thread;  | 
 | 
            if (th != 0)  | 
 | 
                NativeThread.signal(th);  | 
 | 
            if (!isRegistered())  | 
 | 
                kill();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void kill() throws IOException { | 
 | 
        synchronized (stateLock) { | 
 | 
            if (state == ST_KILLED)  | 
 | 
                return;  | 
 | 
            if (state == ST_UNINITIALIZED) { | 
 | 
                state = ST_KILLED;  | 
 | 
                return;  | 
 | 
            }  | 
 | 
            assert !isOpen() && !isRegistered();  | 
 | 
            nd.close(fd);  | 
 | 
            state = ST_KILLED;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public boolean translateReadyOps(int ops, int initialOps,  | 
 | 
                                     SelectionKeyImpl sk) { | 
 | 
        int intOps = sk.nioInterestOps();   | 
 | 
        int oldOps = sk.nioReadyOps();  | 
 | 
        int newOps = initialOps;  | 
 | 
 | 
 | 
        if ((ops & Net.POLLNVAL) != 0) { | 
 | 
            // This should only happen if this channel is pre-closed while a  | 
 | 
            // selection operation is in progress  | 
 | 
              | 
 | 
            return false;  | 
 | 
        }  | 
 | 
 | 
 | 
        if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) { | 
 | 
            newOps = intOps;  | 
 | 
            sk.nioReadyOps(newOps);  | 
 | 
            return (newOps & ~oldOps) != 0;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (((ops & Net.POLLIN) != 0) &&  | 
 | 
            ((intOps & SelectionKey.OP_ACCEPT) != 0))  | 
 | 
                newOps |= SelectionKey.OP_ACCEPT;  | 
 | 
 | 
 | 
        sk.nioReadyOps(newOps);  | 
 | 
        return (newOps & ~oldOps) != 0;  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) { | 
 | 
        return translateReadyOps(ops, sk.nioReadyOps(), sk);  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) { | 
 | 
        return translateReadyOps(ops, 0, sk);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    int poll(int events, long timeout) throws IOException { | 
 | 
        assert Thread.holdsLock(blockingLock()) && !isBlocking();  | 
 | 
 | 
 | 
        synchronized (lock) { | 
 | 
            int n = 0;  | 
 | 
            try { | 
 | 
                begin();  | 
 | 
                synchronized (stateLock) { | 
 | 
                    if (!isOpen())  | 
 | 
                        return 0;  | 
 | 
                    thread = NativeThread.current();  | 
 | 
                }  | 
 | 
                n = Net.poll(fd, events, timeout);  | 
 | 
            } finally { | 
 | 
                thread = 0;  | 
 | 
                end(n > 0);  | 
 | 
            }  | 
 | 
            return n;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) { | 
 | 
        int newOps = 0;  | 
 | 
 | 
 | 
          | 
 | 
        if ((ops & SelectionKey.OP_ACCEPT) != 0)  | 
 | 
            newOps |= Net.POLLIN;  | 
 | 
          | 
 | 
        sk.selector.putEventOps(sk, newOps);  | 
 | 
    }  | 
 | 
 | 
 | 
    public FileDescriptor getFD() { | 
 | 
        return fd;  | 
 | 
    }  | 
 | 
 | 
 | 
    public int getFDVal() { | 
 | 
        return fdVal;  | 
 | 
    }  | 
 | 
 | 
 | 
    public String toString() { | 
 | 
        StringBuffer sb = new StringBuffer();  | 
 | 
        sb.append(this.getClass().getName());  | 
 | 
        sb.append('['); | 
 | 
        if (!isOpen()) { | 
 | 
            sb.append("closed"); | 
 | 
        } else { | 
 | 
            synchronized (stateLock) { | 
 | 
                InetSocketAddress addr = localAddress();  | 
 | 
                if (addr == null) { | 
 | 
                    sb.append("unbound"); | 
 | 
                } else { | 
 | 
                    sb.append(Net.getRevealedLocalAddressAsString(addr));  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        sb.append(']'); | 
 | 
        return sb.toString();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private int accept(FileDescriptor ssfd, FileDescriptor newfd,  | 
 | 
                       InetSocketAddress[] isaa)  | 
 | 
        throws IOException  | 
 | 
    { | 
 | 
        return accept0(ssfd, newfd, isaa);  | 
 | 
    }  | 
 | 
 | 
 | 
    // -- Native methods --  | 
 | 
 | 
 | 
    // Accepts a new connection, setting the given file descriptor to refer to  | 
 | 
    // the new socket and setting isaa[0] to the socket's remote address.  | 
 | 
    // Returns 1 on success, or IOStatus.UNAVAILABLE (if non-blocking and no  | 
 | 
    // connections are pending) or IOStatus.INTERRUPTED.  | 
 | 
      | 
 | 
    private native int accept0(FileDescriptor ssfd, FileDescriptor newfd,  | 
 | 
                               InetSocketAddress[] isaa)  | 
 | 
        throws IOException;  | 
 | 
 | 
 | 
    private static native void initIDs();  | 
 | 
 | 
 | 
    static { | 
 | 
        IOUtil.load();  | 
 | 
        initIDs();  | 
 | 
        nd = new SocketDispatcher();  | 
 | 
    }  | 
 | 
 | 
 | 
}  |