1 /* 2 * Copyright (c) 2008, 2018, 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 import sun.net.ext.ExtendedSocketOptions; 43 import static sun.net.ext.ExtendedSocketOptions.SOCK_STREAM; 44 45 /** 46 * Base implementation of AsynchronousServerSocketChannel. 47 */ 48 49 abstract class AsynchronousServerSocketChannelImpl 50 extends AsynchronousServerSocketChannel 51 implements Cancellable, Groupable 52 { 53 protected final FileDescriptor fd; 54 55 // the local address to which the channel's socket is bound 56 protected volatile InetSocketAddress localAddress; 57 58 // need this lock to set local address 59 private final Object stateLock = new Object(); 60 61 // close support 62 private ReadWriteLock closeLock = new ReentrantReadWriteLock(); 63 private volatile boolean closed; 64 65 // set true when accept operation is cancelled 66 private volatile boolean acceptKilled; 67 68 // set true when exclusive binding is on and SO_REUSEADDR is emulated 69 private boolean isReuseAddress; 70 AsynchronousServerSocketChannelImpl(AsynchronousChannelGroupImpl group)71 AsynchronousServerSocketChannelImpl(AsynchronousChannelGroupImpl group) { 72 super(group.provider()); 73 this.fd = Net.serverSocket(true); 74 } 75 76 @Override isOpen()77 public final boolean isOpen() { 78 return !closed; 79 } 80 81 /** 82 * Marks beginning of access to file descriptor/handle 83 */ begin()84 final void begin() throws IOException { 85 closeLock.readLock().lock(); 86 if (!isOpen()) 87 throw new ClosedChannelException(); 88 } 89 90 /** 91 * Marks end of access to file descriptor/handle 92 */ end()93 final void end() { 94 closeLock.readLock().unlock(); 95 } 96 97 /** 98 * Invoked to close file descriptor/handle. 99 */ implClose()100 abstract void implClose() throws IOException; 101 102 @Override close()103 public final void close() throws IOException { 104 // synchronize with any threads using file descriptor/handle 105 closeLock.writeLock().lock(); 106 try { 107 if (closed) 108 return; // already closed 109 closed = true; 110 } finally { 111 closeLock.writeLock().unlock(); 112 } 113 implClose(); 114 } 115 116 /** 117 * Invoked by accept to accept connection 118 */ 119 abstract Future<AsynchronousSocketChannel> implAccept(Object attachment, CompletionHandler<AsynchronousSocketChannel,Object> handler)120 implAccept(Object attachment, 121 CompletionHandler<AsynchronousSocketChannel,Object> handler); 122 123 124 @Override accept()125 public final Future<AsynchronousSocketChannel> accept() { 126 return implAccept(null, null); 127 } 128 129 @Override 130 @SuppressWarnings("unchecked") accept(A attachment, CompletionHandler<AsynchronousSocketChannel,? super A> handler)131 public final <A> void accept(A attachment, 132 CompletionHandler<AsynchronousSocketChannel,? super A> handler) 133 { 134 if (handler == null) 135 throw new NullPointerException("'handler' is null"); 136 implAccept(attachment, (CompletionHandler<AsynchronousSocketChannel,Object>)handler); 137 } 138 isAcceptKilled()139 final boolean isAcceptKilled() { 140 return acceptKilled; 141 } 142 143 @Override onCancel(PendingFuture<?,?> task)144 public final void onCancel(PendingFuture<?,?> task) { 145 acceptKilled = true; 146 } 147 148 @Override bind(SocketAddress local, int backlog)149 public final AsynchronousServerSocketChannel bind(SocketAddress local, int backlog) 150 throws IOException 151 { 152 InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) : 153 Net.checkAddress(local); 154 SecurityManager sm = System.getSecurityManager(); 155 if (sm != null) 156 sm.checkListen(isa.getPort()); 157 158 try { 159 begin(); 160 synchronized (stateLock) { 161 if (localAddress != null) 162 throw new AlreadyBoundException(); 163 NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort()); 164 Net.bind(fd, isa.getAddress(), isa.getPort()); 165 Net.listen(fd, backlog < 1 ? 50 : backlog); 166 localAddress = Net.localAddress(fd); 167 } 168 } finally { 169 end(); 170 } 171 return this; 172 } 173 174 @Override getLocalAddress()175 public final SocketAddress getLocalAddress() throws IOException { 176 if (!isOpen()) 177 throw new ClosedChannelException(); 178 return Net.getRevealedLocalAddress(localAddress); 179 } 180 181 @Override setOption(SocketOption<T> name, T value)182 public final <T> AsynchronousServerSocketChannel setOption(SocketOption<T> name, 183 T value) 184 throws IOException 185 { 186 if (name == null) 187 throw new NullPointerException(); 188 if (!supportedOptions().contains(name)) 189 throw new UnsupportedOperationException("'" + name + "' not supported"); 190 191 try { 192 begin(); 193 if (name == StandardSocketOptions.SO_REUSEADDR && 194 Net.useExclusiveBind()) 195 { 196 // SO_REUSEADDR emulated when using exclusive bind 197 isReuseAddress = (Boolean)value; 198 } else { 199 Net.setSocketOption(fd, Net.UNSPEC, name, value); 200 } 201 return this; 202 } finally { 203 end(); 204 } 205 } 206 207 @Override 208 @SuppressWarnings("unchecked") getOption(SocketOption<T> name)209 public final <T> T getOption(SocketOption<T> name) throws IOException { 210 if (name == null) 211 throw new NullPointerException(); 212 if (!supportedOptions().contains(name)) 213 throw new UnsupportedOperationException("'" + name + "' not supported"); 214 215 try { 216 begin(); 217 if (name == StandardSocketOptions.SO_REUSEADDR && 218 Net.useExclusiveBind()) 219 { 220 // SO_REUSEADDR emulated when using exclusive bind 221 return (T)Boolean.valueOf(isReuseAddress); 222 } 223 return (T) Net.getSocketOption(fd, Net.UNSPEC, name); 224 } finally { 225 end(); 226 } 227 } 228 229 private static class DefaultOptionsHolder { 230 static final Set<SocketOption<?>> defaultOptions = defaultOptions(); 231 defaultOptions()232 private static Set<SocketOption<?>> defaultOptions() { 233 HashSet<SocketOption<?>> set = new HashSet<>(2); 234 set.add(StandardSocketOptions.SO_RCVBUF); 235 set.add(StandardSocketOptions.SO_REUSEADDR); 236 if (Net.isReusePortAvailable()) { 237 set.add(StandardSocketOptions.SO_REUSEPORT); 238 } 239 set.addAll(ExtendedSocketOptions.options(SOCK_STREAM)); 240 return Collections.unmodifiableSet(set); 241 } 242 } 243 244 @Override supportedOptions()245 public final Set<SocketOption<?>> supportedOptions() { 246 return DefaultOptionsHolder.defaultOptions; 247 } 248 249 @Override toString()250 public final String toString() { 251 StringBuilder sb = new StringBuilder(); 252 sb.append(this.getClass().getName()); 253 sb.append('['); 254 if (!isOpen()) 255 sb.append("closed"); 256 else { 257 if (localAddress == null) { 258 sb.append("unbound"); 259 } else { 260 sb.append(Net.getRevealedLocalAddressAsString(localAddress)); 261 } 262 } 263 sb.append(']'); 264 return sb.toString(); 265 } 266 } 267