1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.nio; 19 20 import java.io.FileDescriptor; 21 import java.io.IOException; 22 import java.net.PlainServerSocketImpl; 23 import java.net.ServerSocket; 24 import java.net.Socket; 25 import java.net.SocketAddress; 26 import java.net.SocketImpl; 27 import java.net.SocketTimeoutException; 28 import java.nio.channels.ClosedChannelException; 29 import java.nio.channels.IllegalBlockingModeException; 30 import java.nio.channels.NotYetBoundException; 31 import java.nio.channels.ServerSocketChannel; 32 import java.nio.channels.SocketChannel; 33 import java.nio.channels.spi.SelectorProvider; 34 import libcore.io.IoUtils; 35 36 /** 37 * The default ServerSocketChannel. 38 */ 39 final class ServerSocketChannelImpl extends ServerSocketChannel implements FileDescriptorChannel { 40 41 private final ServerSocketAdapter socket; 42 private final SocketImpl impl; 43 44 private boolean isBound = false; 45 46 private final Object acceptLock = new Object(); 47 48 public ServerSocketChannelImpl(SelectorProvider sp) throws IOException { 49 super(sp); 50 this.socket = new ServerSocketAdapter(this); 51 this.impl = socket.getImpl$(); 52 } 53 54 @Override public ServerSocket socket() { 55 return socket; 56 } 57 58 @Override public SocketChannel accept() throws IOException { 59 if (!isOpen()) { 60 throw new ClosedChannelException(); 61 } 62 if (!isBound) { 63 throw new NotYetBoundException(); 64 } 65 66 // Create an empty socket channel. This will be populated by ServerSocketAdapter.accept. 67 SocketChannelImpl result = new SocketChannelImpl(provider(), false); 68 boolean connected = false; 69 try { 70 begin(); 71 synchronized (acceptLock) { 72 synchronized (blockingLock()) { 73 do { 74 try { 75 socket.implAccept(result); 76 // select successfully, break out immediately. 77 break; 78 } catch (SocketTimeoutException e) { 79 // continue to accept if the channel is in blocking mode. 80 // TODO: does this make sense? why does blocking imply no timeouts? 81 } 82 } while (isBlocking()); 83 } 84 } 85 } finally { 86 end(result.socket().isConnected()); 87 } 88 return result.socket().isConnected() ? result : null; 89 } 90 91 @Override protected void implConfigureBlocking(boolean blocking) throws IOException { 92 synchronized (blockingLock()) { 93 IoUtils.setBlocking(impl.getFD$(), blocking); 94 } 95 } 96 97 synchronized protected void implCloseSelectableChannel() throws IOException { 98 if (!socket.isClosed()) { 99 socket.close(); 100 } 101 } 102 103 public FileDescriptor getFD() { 104 return impl.getFD$(); 105 } 106 107 private static class ServerSocketAdapter extends ServerSocket { 108 private final ServerSocketChannelImpl channelImpl; 109 110 ServerSocketAdapter(ServerSocketChannelImpl aChannelImpl) throws IOException { 111 this.channelImpl = aChannelImpl; 112 } 113 114 @Override public void bind(SocketAddress localAddress, int backlog) throws IOException { 115 super.bind(localAddress, backlog); 116 channelImpl.isBound = true; 117 } 118 119 @Override public Socket accept() throws IOException { 120 if (!channelImpl.isBound) { 121 throw new IllegalBlockingModeException(); 122 } 123 SocketChannel sc = channelImpl.accept(); 124 if (sc == null) { 125 throw new IllegalBlockingModeException(); 126 } 127 return sc.socket(); 128 } 129 130 public Socket implAccept(SocketChannelImpl clientSocketChannel) throws IOException { 131 Socket clientSocket = clientSocketChannel.socket(); 132 boolean connectOK = false; 133 try { 134 synchronized (this) { 135 super.implAccept(clientSocket); 136 clientSocketChannel.setConnected(); 137 clientSocketChannel.setBound(true); 138 clientSocketChannel.finishAccept(); 139 } 140 connectOK = true; 141 } finally { 142 if (!connectOK) { 143 clientSocket.close(); 144 } 145 } 146 return clientSocket; 147 } 148 149 @Override public ServerSocketChannel getChannel() { 150 return channelImpl; 151 } 152 153 @Override public boolean isBound() { 154 return channelImpl.isBound; 155 } 156 157 @Override public void bind(SocketAddress localAddress) throws IOException { 158 super.bind(localAddress); 159 channelImpl.isBound = true; 160 } 161 162 @Override public void close() throws IOException { 163 synchronized (channelImpl) { 164 if (channelImpl.isOpen()) { 165 channelImpl.close(); 166 } else { 167 super.close(); 168 } 169 } 170 } 171 } 172 } 173