/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.nio.internal; // BEGIN android-note // Copied from a newer version of Harmony. // END android-note import java.io.FileDescriptor; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketImpl; import java.net.SocketTimeoutException; import java.nio.channels.ClosedChannelException; import java.nio.channels.IllegalBlockingModeException; import java.nio.channels.NotYetBoundException; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.channels.spi.SelectorProvider; import org.apache.harmony.luni.net.NetUtil; import org.apache.harmony.luni.net.PlainServerSocketImpl; import org.apache.harmony.luni.platform.FileDescriptorHandler; import org.apache.harmony.luni.platform.Platform; /* * The default implementation class of java.nio.channels.ServerSocketChannel. */ public class ServerSocketChannelImpl extends ServerSocketChannel implements FileDescriptorHandler { // status un-init, not initialized. private static final int SERVER_STATUS_UNINIT = -1; // status after open and before closed. private static final int SERVER_STATUS_OPEN = 0; // status closed. private static final int SERVER_STATUS_CLOSED = 1; // The fd to interact with native code private final FileDescriptor fd; // The internal ServerSocket private final ServerSocket socket; private final SocketImpl impl; int status = SERVER_STATUS_UNINIT; // whether the socket is bound boolean isBound = false; // lock for accept private static class AcceptLock {} private final Object acceptLock = new AcceptLock(); /* * Constructor */ public ServerSocketChannelImpl(SelectorProvider sp) throws IOException { super(sp); status = SERVER_STATUS_OPEN; fd = new FileDescriptor(); Platform.getNetworkSystem().createStreamSocket(fd, NetUtil.preferIPv4Stack()); impl = new PlainServerSocketImpl(fd); socket = new ServerSocketAdapter(impl, this); } // for native call @SuppressWarnings("unused") private ServerSocketChannelImpl() throws IOException { super(SelectorProvider.provider()); status = SERVER_STATUS_OPEN; fd = new FileDescriptor(); impl = new PlainServerSocketImpl(fd); socket = new ServerSocketAdapter(impl, this); isBound = false; } /* * Getting the internal Socket If we have not the socket, we create a new * one. */ public ServerSocket socket() { return socket; } /* * * @see java.nio.channels.ServerSocketChannel#accept() */ public SocketChannel accept() throws IOException { if (!isOpen()) { throw new ClosedChannelException(); } if (!isBound) { throw new NotYetBoundException(); } SocketChannel sockChannel = new SocketChannelImpl(SelectorProvider.provider(), false); Socket socketGot = sockChannel.socket(); try { begin(); synchronized (acceptLock) { synchronized (blockingLock()) { boolean isBlocking = isBlocking(); if (!isBlocking) { // for non blocking mode, use select to see whether // there are any pending connections. int[] tryResult = Platform.getNetworkSystem().select( new FileDescriptor[] { this.fd }, new FileDescriptor[0], 0); if (0 == tryResult.length || 0 == tryResult[0]) { // no pending connections, returns immediately. return null; } } // do accept. do { try { ((ServerSocketAdapter) socket).accept(socketGot, (SocketChannelImpl) sockChannel); // select successfully, break out immediately. break; } catch (SocketTimeoutException e) { // continue to accept if the channel is in blocking // mode. } } while (isBlocking); } } } finally { end(socketGot.isConnected()); } return sockChannel; } /* * @see java.nio.channels.spi.AbstractSelectableChannel#implConfigureBlocking * * (boolean) */ protected void implConfigureBlocking(boolean blockingMode) throws IOException { // Do nothing here. For real accept() operation in nonblocking mode, // it uses INetworkSystem.select. Whether a channel is blocking can be // decided by isBlocking() method. } /* * * @see java.nio.channels.spi.AbstractSelectableChannel#implCloseSelectableChannel() */ synchronized protected void implCloseSelectableChannel() throws IOException { status = SERVER_STATUS_CLOSED; if (!socket.isClosed()) { socket.close(); } } /* * Gets the FileDescriptor */ public FileDescriptor getFD() { return fd; } /* * The adapter class of ServerSocket. */ private class ServerSocketAdapter extends ServerSocket { /* * The related ServerSocketChannel. */ ServerSocketChannelImpl channelImpl; /* * The Constructor. */ ServerSocketAdapter(SocketImpl impl, ServerSocketChannelImpl aChannelImpl) { super(impl); this.channelImpl = aChannelImpl; } /* * * @see java.net.ServerSocket#bind(java.net.SocketAddress, int) */ public void bind(SocketAddress localAddr, int backlog) throws IOException { super.bind(localAddr, backlog); channelImpl.isBound = true; } /* * @see java.net.ServerSocket#accept() * * If the channel is in non-blocking mode and there is no connection * ready to be accepted, invoking this method will cause an * IllegalBlockingModeException. */ public Socket accept() throws IOException { if (!isBound) { throw new IllegalBlockingModeException(); } SocketChannel sc = channelImpl.accept(); if (null == sc) { throw new IllegalBlockingModeException(); } return sc.socket(); } /* * do the accept. */ private Socket accept(Socket aSocket, SocketChannelImpl sockChannel) throws IOException { // a new socket is pass in so we do not need to "Socket aSocket = // new Socket();" boolean connectOK = false; try { synchronized (this) { super.implAccept(aSocket); sockChannel.setConnected(); sockChannel.setBound(true); } SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkAccept(aSocket.getInetAddress().getHostAddress(), aSocket.getPort()); } connectOK = true; } finally { if (!connectOK) { aSocket.close(); } } return aSocket; } /* * getting internal channel. */ public ServerSocketChannel getChannel() { return channelImpl; } /* * * @see java.net.ServerSocket#isBound() */ public boolean isBound() { return channelImpl.isBound; } /* * * @see java.net.ServerSocket#bind(java.net.SocketAddress) */ public void bind(SocketAddress localAddr) throws IOException { super.bind(localAddr); channelImpl.isBound = true; } /* * @see java.net.ServerSocket#close() */ public void close() throws IOException { synchronized (channelImpl) { if (channelImpl.isOpen()) { channelImpl.close(); } else { super.close(); } channelImpl.status = SERVER_STATUS_CLOSED; } } } }