/*
* Copyright 2012 The Netty Project
* Copyright 2013 Red Hat, Inc.
*
* The Netty Project 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.fusesource.hawtdispatch.netty;
import io.netty.buffer.BufType;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.socket.DefaultServerSocketChannelConfig;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.ServerSocketChannelConfig;
import io.netty.util.internal.InternalLogger;
import io.netty.util.internal.InternalLoggerFactory;
import org.fusesource.hawtdispatch.*;
import static java.nio.channels.SelectionKey.*;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.SocketChannel;
/**
* {@link ServerSocketChannel} implementation which uses HawtDispatch.
*
* @author <a href="mailto:nmaurer@redhat.com">Norman Maurer</a>
*/
public class HawtServerSocketChannel extends HawtAbstractChannel implements ServerSocketChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.MESSAGE, false);
private static final InternalLogger logger =
InternalLoggerFactory.getInstance(HawtServerSocketChannel.class);
private DispatchSource acceptSource;
private static java.nio.channels.ServerSocketChannel newSocket() {
try {
return java.nio.channels.ServerSocketChannel.open();
} catch (IOException e) {
throw new ChannelException(
"Failed to open a server socket.", e);
}
}
private final ServerSocketChannelConfig config;
/**
* Create a new instance
*/
public HawtServerSocketChannel() throws IOException {
super(null, null, newSocket());
javaChannel().configureBlocking(false);
config = new DefaultServerSocketChannelConfig(this, javaChannel().socket());
}
@Override
protected java.nio.channels.ServerSocketChannel javaChannel() {
return (java.nio.channels.ServerSocketChannel) super.javaChannel();
}
@Override
public boolean isActive() {
return ch != null && javaChannel().isOpen() && localAddress0() != null;
}
@Override
public ChannelMetadata metadata() {
return METADATA;
}
@Override
protected SocketAddress localAddress0() {
return javaChannel().socket().getLocalSocketAddress();
}
@Override
protected SocketAddress remoteAddress0() {
return null;
}
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
@Override
protected Runnable doRegister() throws Exception {
final Runnable task = super.doRegister();
return new Runnable() {
@Override
public void run() {
if (task != null) {
task.run();
}
// Create the source and register the handlers to it
acceptSource = createSource(OP_ACCEPT);
acceptSource.setEventHandler(new Task() {
@Override
public void run() {
boolean added = false;
for (;;) {
try {
SocketChannel channel = javaChannel().accept();
if (channel == null) {
break;
}
pipeline().inboundMessageBuffer().add(
new HawtSocketChannel(HawtServerSocketChannel.this, null, channel));
added = true;
} catch (IOException e) {
if (isOpen()) {
logger.warn("Failed to create a new channel from an accepted socket.", e);
}
break;
}
}
if (added) {
pipeline().fireInboundBufferUpdated();
pipeline().fireChannelReadSuspended();
}
// suspend accepts if needed
if (!config().isAutoRead()) {
acceptSource.suspend();
}
}
});
closeFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
acceptSource.cancel();
}
});
}
};
}
@Override
protected void doBeginRead() {
if (acceptSource.isSuspended() && !acceptSource.isCanceled()) {
acceptSource.resume();
}
}
@Override
protected void doClose() throws Exception {
super.doClose();
javaChannel().close();
}
@Override
protected boolean isFlushPending() {
return false;
}
@Override
protected boolean doConnect(
SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
throw new UnsupportedOperationException();
}
@Override
protected void doFinishConnect() throws Exception {
throw new UnsupportedOperationException();
}
@Override
protected void doDisconnect() throws Exception {
throw new UnsupportedOperationException();
}
@Override
public ServerSocketChannelConfig config() {
return config;
}
}