package org.fastcatsearch.transport.common; import java.net.SocketAddress; import java.util.ArrayDeque; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.DownstreamMessageEvent; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class HandshakeHandler extends SimpleChannelHandler { protected static Logger logger = LoggerFactory.getLogger(HandshakeHandler.class); protected final long timeoutInMillis; protected final String localId; protected final AtomicBoolean handshakeComplete; protected final AtomicBoolean handshakeFailed; protected final CountDownLatch latch = new CountDownLatch(1); protected final Queue<MessageEvent> messages = new ArrayDeque<MessageEvent>(); protected final Object handshakeMutex = new Object(); protected String challenge; public HandshakeHandler(String localId, long timeoutInMillis) { this.localId = localId; this.timeoutInMillis = timeoutInMillis; this.handshakeComplete = new AtomicBoolean(false); this.handshakeFailed = new AtomicBoolean(false); } @Override public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { logger.debug("Channel이 닫혔습니다."); if (!handshakeComplete.get()) { fireHandshakeFailed(ctx); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { logger.error("Channel에서 Exception 발생.", e.getCause()); if (e.getChannel().isConnected()) { e.getChannel().close(); } else { fireHandshakeFailed(ctx); } } @Override public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception { synchronized (handshakeMutex) { if (handshakeFailed.get()) { // handshake 가 실패였으면, 메시지를 보내지않고 무시한다. return; } if (handshakeComplete.get()) { super.writeRequested(ctx, e); } else { // handshake가 아직 완료되지 않았으면, 메시지를 큐에 쌓아둔다. // 나중에 완료시 일괄 전송하게 된다. messages.offer(e); } } } protected void writeDownstream(ChannelHandlerContext ctx, Object data) { ChannelFuture f = Channels.succeededFuture(ctx.getChannel()); SocketAddress address = ctx.getChannel().getRemoteAddress(); Channel c = ctx.getChannel(); ctx.sendDownstream(new DownstreamMessageEvent(c, f, data, address)); } protected void fireHandshakeFailed(ChannelHandlerContext ctx) { handshakeComplete.set(true); handshakeFailed.set(true); latch.countDown(); ctx.getChannel().close(); ctx.sendUpstream(HandshakeEvent.handshakeFailed(ctx.getChannel())); } protected void fireHandshakeSucceeded(String server, ChannelHandlerContext ctx) { handshakeComplete.set(true); handshakeFailed.set(false); latch.countDown(); ctx.sendUpstream(HandshakeEvent.handshakeSucceeded(server, ctx.getChannel())); } }