blob: 3dcd4cf3cdbe4c5dadeb5533f9e07f95185ffa16 [file] [log] [blame]
/*
* 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;
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 = new int[1];
boolean success = Platform.getNetworkSystem().select(
new FileDescriptor[] { this.fd },
new FileDescriptor[0], 1, 0, 0, tryResult);
if (!success || 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;
}
}
}
}