Home | History | Annotate | Download | only in internal
      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 org.apache.harmony.nio.internal;
     19 
     20 import java.io.FileDescriptor;
     21 import java.io.IOException;
     22 import java.net.ServerSocket;
     23 import java.net.Socket;
     24 import java.net.SocketAddress;
     25 import java.net.SocketImpl;
     26 import java.net.SocketTimeoutException;
     27 import java.nio.channels.ClosedChannelException;
     28 import java.nio.channels.IllegalBlockingModeException;
     29 import java.nio.channels.NotYetBoundException;
     30 import java.nio.channels.ServerSocketChannel;
     31 import java.nio.channels.SocketChannel;
     32 import java.nio.channels.spi.SelectorProvider;
     33 import org.apache.harmony.luni.net.PlainServerSocketImpl;
     34 import org.apache.harmony.luni.platform.FileDescriptorHandler;
     35 import org.apache.harmony.luni.platform.Platform;
     36 
     37 /**
     38  * The default ServerSocketChannel.
     39  */
     40 public final class ServerSocketChannelImpl
     41         extends ServerSocketChannel implements FileDescriptorHandler {
     42 
     43     private final FileDescriptor fd = new FileDescriptor();
     44     private final SocketImpl impl = new PlainServerSocketImpl(fd);
     45     private final ServerSocketAdapter socket = new ServerSocketAdapter(impl, this);
     46 
     47     private boolean isBound = false;
     48 
     49     private static class AcceptLock {}
     50     private final Object acceptLock = new AcceptLock();
     51 
     52     public ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
     53         super(sp);
     54     }
     55 
     56     // for native call
     57     @SuppressWarnings("unused")
     58     private ServerSocketChannelImpl() throws IOException {
     59         this(SelectorProvider.provider());
     60     }
     61 
     62     @Override public ServerSocket socket() {
     63         return socket;
     64     }
     65 
     66     @Override public SocketChannel accept() throws IOException {
     67         if (!isOpen()) {
     68             throw new ClosedChannelException();
     69         }
     70         if (!isBound) {
     71             throw new NotYetBoundException();
     72         }
     73 
     74         // TODO: pass in the SelectorProvider used to create this ServerSocketChannelImpl?
     75         // Create an empty socket channel. This will be populated by ServerSocketAdapter.accept.
     76         SocketChannelImpl result = new SocketChannelImpl(SelectorProvider.provider(), false);
     77         Socket resultSocket = result.socket();
     78 
     79         try {
     80             begin();
     81             synchronized (acceptLock) {
     82                 synchronized (blockingLock()) {
     83                     boolean isBlocking = isBlocking();
     84                     if (!isBlocking) {
     85                         int[] tryResult = new int[1];
     86                         boolean success = Platform.getNetworkSystem().select(
     87                                 new FileDescriptor[] { fd },
     88                                 new FileDescriptor[0], 1, 0, 0, tryResult);
     89                         if (!success || 0 == tryResult[0]) {
     90                             // no pending connections, returns immediately.
     91                             return null;
     92                         }
     93                     }
     94                     // do accept.
     95                     do {
     96                         try {
     97                             socket.accept(resultSocket, result);
     98                             // select successfully, break out immediately.
     99                             break;
    100                         } catch (SocketTimeoutException e) {
    101                             // continue to accept if the channel is in blocking mode.
    102                         }
    103                     } while (isBlocking);
    104                 }
    105             }
    106         } finally {
    107             end(resultSocket.isConnected());
    108         }
    109         return result;
    110     }
    111 
    112     protected void implConfigureBlocking(boolean blockingMode) throws IOException {
    113         // Do nothing here. For real accept() operation in non-blocking mode,
    114         // it uses INetworkSystem.select. Whether a channel is blocking can be
    115         // decided by isBlocking() method.
    116     }
    117 
    118     synchronized protected void implCloseSelectableChannel() throws IOException {
    119         if (!socket.isClosed()) {
    120             socket.close();
    121         }
    122     }
    123 
    124     public FileDescriptor getFD() {
    125         return fd;
    126     }
    127 
    128     private static class ServerSocketAdapter extends ServerSocket {
    129         private final ServerSocketChannelImpl channelImpl;
    130 
    131         ServerSocketAdapter(SocketImpl impl, ServerSocketChannelImpl aChannelImpl) {
    132             super(impl);
    133             this.channelImpl = aChannelImpl;
    134         }
    135 
    136         @Override public void bind(SocketAddress localAddress, int backlog) throws IOException {
    137             super.bind(localAddress, backlog);
    138             channelImpl.isBound = true;
    139         }
    140 
    141         @Override public Socket accept() throws IOException {
    142             if (!channelImpl.isBound) {
    143                 throw new IllegalBlockingModeException();
    144             }
    145             SocketChannel sc = channelImpl.accept();
    146             if (null == sc) {
    147                 throw new IllegalBlockingModeException();
    148             }
    149             return sc.socket();
    150         }
    151 
    152         private Socket accept(Socket socket, SocketChannelImpl sockChannel) throws IOException {
    153             boolean connectOK = false;
    154             try {
    155                 synchronized (this) {
    156                     super.implAccept(socket);
    157                     sockChannel.setConnected();
    158                     sockChannel.setBound(true);
    159                     sockChannel.finishAccept();
    160                 }
    161                 SecurityManager sm = System.getSecurityManager();
    162                 if (sm != null) {
    163                     sm.checkAccept(socket.getInetAddress().getHostAddress(), socket.getPort());
    164                 }
    165                 connectOK = true;
    166             } finally {
    167                 if (!connectOK) {
    168                     socket.close();
    169                 }
    170             }
    171             return socket;
    172         }
    173 
    174         @Override public ServerSocketChannel getChannel() {
    175             return channelImpl;
    176         }
    177 
    178         @Override public boolean isBound() {
    179             return channelImpl.isBound;
    180         }
    181 
    182         @Override public void bind(SocketAddress localAddress) throws IOException {
    183             super.bind(localAddress);
    184             channelImpl.isBound = true;
    185         }
    186 
    187         @Override public void close() throws IOException {
    188             synchronized (channelImpl) {
    189                 if (channelImpl.isOpen()) {
    190                     channelImpl.close();
    191                 } else {
    192                     super.close();
    193                 }
    194             }
    195         }
    196     }
    197 }
    198