/*
* Copyright 2007-2010 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*/
package com.sun.sgs.impl.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.UnresolvedAddressException;
import java.nio.channels.UnsupportedAddressTypeException;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import com.sun.sgs.nio.channels.AlreadyBoundException;
import com.sun.sgs.nio.channels.AsynchronousServerSocketChannel;
import com.sun.sgs.nio.channels.AsynchronousSocketChannel;
import com.sun.sgs.nio.channels.CompletionHandler;
import com.sun.sgs.nio.channels.IoFuture;
import com.sun.sgs.nio.channels.SocketOption;
import com.sun.sgs.nio.channels.StandardSocketOption;
import static java.nio.channels.SelectionKey.OP_ACCEPT;
/**
* An implementation of {@link AsynchronousServerSocketChannel}.
* Most interesting methods are delegated to the {@link AsyncKey}
* returned by this channel's channel group.
*/
final class AsyncServerSocketChannelImpl
extends AsynchronousServerSocketChannel
{
/** The valid socket options for this channel. */
private static final Set<SocketOption> socketOptions;
static {
Set<? extends SocketOption> es = EnumSet.of(
StandardSocketOption.SO_RCVBUF,
StandardSocketOption.SO_REUSEADDR);
socketOptions = Collections.unmodifiableSet(es);
}
/** The channel group. */
final AsyncGroupImpl group;
/** The underlying {@code ServerSocketChannel}. */
final ServerSocketChannel channel;
/** The {@code AsyncKey} for the underlying channel. */
final AsyncKey key;
/**
* Creates a new instance registered with the given channel group.
*
* @param group the channel group
* @throws IOException if an I/O error occurs
*/
AsyncServerSocketChannelImpl(AsyncGroupImpl group)
throws IOException
{
super(group.provider());
this.group = group;
channel = group.selectorProvider().openServerSocketChannel();
key = group.register(channel);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return super.toString() + ":" + key;
}
/**
* {@inheritDoc}
*/
public boolean isOpen() {
return channel.isOpen();
}
/**
* {@inheritDoc}
*/
@Override
public void close() throws IOException {
key.close();
}
/**
* {@inheritDoc}
*/
@Override
public AsyncServerSocketChannelImpl bind(SocketAddress local,
int backlog)
throws IOException
{
if ((local != null) && (!(local instanceof InetSocketAddress))) {
throw new UnsupportedAddressTypeException();
}
InetSocketAddress inetLocal = (InetSocketAddress) local;
if ((inetLocal != null) && inetLocal.isUnresolved()) {
throw new UnresolvedAddressException();
}
final ServerSocket socket = channel.socket();
try {
socket.bind(local, backlog);
} catch (SocketException e) {
if (socket.isBound()) {
throw Util.initCause(new AlreadyBoundException(), e);
}
if (socket.isClosed()) {
throw Util.initCause(new ClosedChannelException(), e);
}
throw e;
}
return this;
}
/**
* {@inheritDoc}
*/
public SocketAddress getLocalAddress() throws IOException {
return channel.socket().getLocalSocketAddress();
}
/**
* {@inheritDoc}
*/
@Override
public AsyncServerSocketChannelImpl setOption(SocketOption name,
Object value)
throws IOException
{
if (!(name instanceof StandardSocketOption)) {
throw new IllegalArgumentException("Unsupported option " + name);
}
if (value == null || !name.type().isAssignableFrom(value.getClass())) {
throw new IllegalArgumentException("Bad parameter for " + name);
}
StandardSocketOption stdOpt = (StandardSocketOption) name;
final ServerSocket socket = channel.socket();
try {
switch (stdOpt) {
case SO_RCVBUF:
socket.setReceiveBufferSize(((Integer) value).intValue());
break;
case SO_REUSEADDR:
socket.setReuseAddress(((Boolean) value).booleanValue());
break;
default:
throw new IllegalArgumentException("Unsupported option "
+ name);
}
} catch (SocketException e) {
if (socket.isClosed()) {
throw Util.initCause(new ClosedChannelException(), e);
}
throw e;
}
return this;
}
/**
* {@inheritDoc}
*/
public Object getOption(SocketOption name) throws IOException {
if (!(name instanceof StandardSocketOption)) {
throw new IllegalArgumentException("Unsupported option " + name);
}
StandardSocketOption stdOpt = (StandardSocketOption) name;
final ServerSocket socket = channel.socket();
try {
switch (stdOpt) {
case SO_RCVBUF:
return socket.getReceiveBufferSize();
case SO_REUSEADDR:
return socket.getReuseAddress();
default:
throw new IllegalArgumentException("Unsupported option "
+ name);
}
} catch (SocketException e) {
if (socket.isClosed()) {
throw Util.initCause(new ClosedChannelException(), e);
}
throw e;
}
}
/**
* {@inheritDoc}
*/
public Set<SocketOption> options() {
return socketOptions;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isAcceptPending() {
return key.isOpPending(OP_ACCEPT);
}
/**
* {@inheritDoc}
*/
@Override
public <A> IoFuture<AsynchronousSocketChannel, A> accept(
A attachment,
CompletionHandler<AsynchronousSocketChannel, ? super A> handler)
{
return key.execute(
OP_ACCEPT, attachment, handler, 0, TimeUnit.MILLISECONDS,
new Callable<AsynchronousSocketChannel>() {
public AsynchronousSocketChannel call() throws IOException {
try {
SocketChannel newChannel = channel.accept();
if (newChannel == null) {
// TODO re-execute on the key somehow? -JM
throw new IOException("accept failed");
}
return new AsyncSocketChannelImpl(group, newChannel);
} catch (ClosedChannelException e) {
throw Util.initCause(
new AsynchronousCloseException(), e);
}
} });
}
}