package code.google.nfs.rpc.netty.server;
/**
* nfs-rpc
* Apache License
*
* http://code.google.com/p/nfs-rpc (c) 2011
*/
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import code.google.nfs.rpc.ProtocolFactory;
import code.google.nfs.rpc.RequestWrapper;
import code.google.nfs.rpc.ResponseWrapper;
/**
* Netty Server Handler
*
* @author <a href="mailto:bluedavy@gmail.com">bluedavy</a>
*/
public class NettyServerHandler extends SimpleChannelUpstreamHandler {
private static final Log LOGGER = LogFactory.getLog(NettyServerHandler.class);
private ExecutorService threadpool;
public NettyServerHandler(ExecutorService threadpool){
this.threadpool = threadpool;
}
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
if(!(e.getCause() instanceof IOException)){
// only log
LOGGER.error("catch some exception not IOException",e.getCause());
}
}
public void messageReceived(final ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
Object message = e.getMessage();
if (!(message instanceof RequestWrapper) && !(message instanceof List)) {
LOGGER.error("receive message error,only support RequestWrapper || List");
throw new Exception(
"receive message error,only support RequestWrapper || List");
}
handleRequest(ctx,message);
}
@SuppressWarnings("unchecked")
private void handleRequest(final ChannelHandlerContext ctx, final Object message) {
try {
threadpool.execute(new HandlerRunnable(ctx, message, threadpool));
}
catch (RejectedExecutionException exception) {
LOGGER.error("server threadpool full,threadpool maxsize is:"
+ ((ThreadPoolExecutor) threadpool).getMaximumPoolSize());
if(message instanceof List){
List<RequestWrapper> requests = (List<RequestWrapper>) message;
for (final RequestWrapper request : requests) {
sendErrorResponse(ctx, request);
}
}
else{
sendErrorResponse(ctx, (RequestWrapper) message);
}
}
}
private void sendErrorResponse(final ChannelHandlerContext ctx,final RequestWrapper request) {
ResponseWrapper responseWrapper = new ResponseWrapper(request.getId(),request.getCodecType(),request.getProtocolType());
responseWrapper
.setException(new Exception("server threadpool full,maybe because server is slow or too many requests"));
ChannelFuture wf = ctx.getChannel().write(responseWrapper);
wf.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if(!future.isSuccess()){
LOGGER.error("server write response error,request id is: "+request.getId());
}
}
});
}
class HandlerRunnable implements Runnable{
private ChannelHandlerContext ctx;
private Object message;
private ExecutorService threadPool;
public HandlerRunnable(ChannelHandlerContext ctx,Object message,ExecutorService threadPool){
this.ctx = ctx;
this.message = message;
this.threadPool = threadPool;
}
@SuppressWarnings("rawtypes")
public void run() {
// pipeline
if(message instanceof List){
List messages = (List) message;
for (Object messageObject : messages) {
threadPool.execute(new HandlerRunnable(ctx, messageObject, threadPool));
}
}
else{
RequestWrapper request = (RequestWrapper)message;
long beginTime = System.currentTimeMillis();
ResponseWrapper responseWrapper = ProtocolFactory.getServerHandler(request.getProtocolType()).handleRequest(request);
final int id = request.getId();
// already timeout,so not return
if ((System.currentTimeMillis() - beginTime) >= request.getTimeout()) {
LOGGER.warn("timeout,so give up send response to client,requestId is:"
+ id
+ ",client is:"
+ ctx.getChannel().getRemoteAddress()+",consumetime is:"+(System.currentTimeMillis() - beginTime)+",timeout is:"+request.getTimeout());
return;
}
ChannelFuture wf = ctx.getChannel().write(responseWrapper);
wf.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if(!future.isSuccess()){
LOGGER.error("server write response error,request id is: " + id);
}
}
});
}
}
}
}