package org.jboss.netty.bootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelConfig;
import org.jboss.netty.channel.ChannelException;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ChildChannelStateEvent;
import org.jboss.netty.channel.DefaultChannelFuture;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.ServerChannelFactory;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import static org.jboss.netty.channel.Channels.*;
/**
* ��ǰ��ClientBootstrap��Ӧ�ģ������Ƿ������˵���������ServerBootsrapֻ������������ӵĴ��䣬
* ��TCP��local transport���������UDP�����ӵĴ���Ӧ����ConnectionlessBootstrap��
* ��Ϊ����UDP���ԶԵ������������ֱ�ӽ�����Ϣ�������Ǵ���һ����ͨ��������ÿ���ͻ���
* �ڷ������ˣ�һ����ͨ��ָ���Ǽ������ֶ�Ӧ��ͨ����������������parent channel ��ͨ��
* ��bootstrap��ChannelFactory ����bind�����ɵġ�һ���ɹ���
* ���parent Channel�Ϳ��Խ�������Ӧ�ľʹ�����ͨ����
*/
public class ServerBootstrap extends Bootstrap {
private volatile ChannelHandler parentHandler;
/**
* Creates a new instance with no ChannelFactory set.
* setFactory(ChannelFactory) must be called before any I/O operation is requested.
*/
public ServerBootstrap() {
}
/**
* Creates a new instance with the specified initial ChannelFactory.
*/
public ServerBootstrap(ChannelFactory channelFactory) {
super(channelFactory);
}
/**
* ����server�˵�ServerChannelFactory������ִ��IO������
* �÷���ֻ�ܱ�����һ�Σ�������캯��ָ���ˣ��Ͳ����ٴ��趨��
*/
@Override
public void setFactory(ChannelFactory factory) {
if (factory == null) {
throw new NullPointerException("factory");
}
if (!(factory instanceof ServerChannelFactory)) {
throw new IllegalArgumentException(
"factory must be a " +
ServerChannelFactory.class.getSimpleName() + ": " +
factory.getClass());
}
super.setFactory(factory);
}
/**
* Returns an optional {@link ChannelHandler} which intercepts an event
* of a newly bound server-side channel which accepts incoming connections.
*
* @return the parent channel handler.
* {@code null} if no parent channel handler is set.
*/
public ChannelHandler getParentHandler() {
return parentHandler;
}
/**
* Sets an optional {@link ChannelHandler} which intercepts an event of
* a newly bound server-side channel which accepts incoming connections.
*
* @param parentHandler
* the parent channel handler.
* {@code null} to unset the current parent channel handler.
*/
public void setParentHandler(ChannelHandler parentHandler) {
this.parentHandler = parentHandler;
}
/**
* ����һ��Channel�����ѡ��"localAddress"ָ�������ֵ�ַ��
*/
public Channel bind() {
SocketAddress localAddress = (SocketAddress) getOption("localAddress");
if (localAddress == null) {
throw new IllegalStateException("localAddress option is not set.");
}
return bind(localAddress);
}
/**
* ͬ�ϣ�ֻ�ǰ�ʱ��ָ����ַ��
*/
public Channel bind(final SocketAddress localAddress) {
ChannelFuture future = bindAsync(localAddress);
// Wait for the future.
future.awaitUninterruptibly();// �ȴ�ChannelFuture��ɡ�
if (!future.isSuccess()) {
future.getChannel().close().awaitUninterruptibly();
throw new ChannelException("Failed to bind to: " + localAddress, future.getCause());
}
return future.getChannel();
}
/**
* Bind a channel asynchronous to the local address
* specified in the current {@code "localAddress"} option. This method is
* similar to the following code:
*
* <pre>
* {@link ServerBootstrap} b = ...;
* b.bindAsync(b.getOption("localAddress"));
* </pre>
*
*
* @return a new {@link ChannelFuture} which will be notified once the Channel is
* bound and accepts incoming connections
*
* @throws IllegalStateException
* if {@code "localAddress"} option was not set
* @throws ClassCastException
* if {@code "localAddress"} option's value is
* neither a {@link SocketAddress} nor {@code null}
* @throws ChannelException
* if failed to create a new channel and
* bind it to the local address
*/
public ChannelFuture bindAsync() {
SocketAddress localAddress = (SocketAddress) getOption("localAddress");
if (localAddress == null) {
throw new IllegalStateException("localAddress option is not set.");
}
return bindAsync(localAddress);
}
/**
* �첽����ִ��ͨ����
* Bind a channel asynchronous to the specified local address.
*
*���ص�ChannelFuture������ͨ���ɹ������Խ�����������õ�֪ͨ��
*
*/
public ChannelFuture bindAsync(final SocketAddress localAddress) {
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
// �൱�ڶ��ؼ̳е�ʵ��
Binder binder = new Binder(localAddress);
ChannelHandler parentHandler = getParentHandler();
ChannelPipeline bossPipeline = pipeline(); //����һ��Ĭ�ϵ�Pipeline��
// ע����������ͨ����ˮ��������һ��binder����
bossPipeline.addLast("binder", binder);
if (parentHandler != null) {
bossPipeline.addLast("userHandler", parentHandler);
}
// ������ͨ������������ͨ������������pipeline��bossPipeline��
// ���� NioServerSocketChannel(this, pipeline, sink, bossPool.nextBoss(), workerPool);
Channel channel = getFactory().newChannel(bossPipeline);
// ����һ������ȡ���İ�Future
final ChannelFuture bfuture = new DefaultChannelFuture(channel, false);
// ��Binder�����е�Future����һ���۲��ߣ�����Ǹ�Future�ɹ��Ļ��������bfuture�ͻ�ɹ�
// ���Ի��ǿ��DZߵ� ��ʱ�ɹ���
binder.bindFuture.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
bfuture.setSuccess();
} else {
// Call close on bind failure
bfuture.getChannel().close();
bfuture.setFailure(future.getCause());
}
}
});
return bfuture;
}
// Ҳ�̳��� SimpleChannelUpstreamHandler
private final class Binder extends SimpleChannelUpstreamHandler {
private final SocketAddress localAddress;
private final Map<String, Object> childOptions = new HashMap<String, Object>();
private final DefaultChannelFuture bindFuture = new DefaultChannelFuture(null, false);
Binder(SocketAddress localAddress) {
this.localAddress = localAddress;
}
/**
* �� handleUpstream �����лᴥ��
*/
@Override
public void channelOpen( ChannelHandlerContext ctx, ChannelStateEvent evt) {
try {
// �ڲ�����Է����ⲿ��ķ��� ���� getPipelineFactory()��
evt.getChannel().getConfig().setPipelineFactory(getPipelineFactory());
// Split options into two categories: parent and child.
Map<String, Object> allOptions = getOptions();
Map<String, Object> parentOptions = new HashMap<String, Object>();
for (Entry<String, Object> e: allOptions.entrySet()) {
if (e.getKey().startsWith("child.")) {
childOptions.put( e.getKey().substring(6), e.getValue());
} else if (!"pipelineFactory".equals(e.getKey())) {
parentOptions.put(e.getKey(), e.getValue());
}
}
// Apply parent options.
evt.getChannel().getConfig().setOptions(parentOptions);
} finally {
ctx.sendUpstream(evt);
}
//============ ���������� =======
evt.getChannel().bind(localAddress).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
// ���ﴥ���� bind�ijɹ�
bindFuture.setSuccess();
} else {
bindFuture.setFailure(future.getCause());
}
}
});
}
@Override
public void childChannelOpen(
ChannelHandlerContext ctx,
ChildChannelStateEvent e) throws Exception {
// Apply child options.
try {
e.getChildChannel().getConfig().setOptions(childOptions);
} catch (Throwable t) {
fireExceptionCaught(e.getChildChannel(), t);
}
ctx.sendUpstream(e);
}
@Override
public void exceptionCaught(
ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
bindFuture.setFailure(e.getCause());
ctx.sendUpstream(e);
}
}
}