package com.lenzhao.framework.server;

import java.lang.reflect.Method;

import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.group.ChannelGroup;

import com.lenzhao.framework.protocol.RpcRequest;
import com.lenzhao.framework.protocol.RpcResponse;
import com.lenzhao.framework.util.ReflectionCache;
import org.jboss.netty.handler.timeout.ReadTimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *RPC具体处理类
 */
public class NettyRpcServerHandler extends SimpleChannelUpstreamHandler {

	private static final Logger logger = LoggerFactory.getLogger(NettyRpcServerHandler.class);

	private final ChannelGroup channelGroups;

	public NettyRpcServerHandler() {
		this.channelGroups = null;
	}

	public NettyRpcServerHandler(ChannelGroup channelGroups) {
		this.channelGroups = channelGroups;
	}

	@Override
	public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
			throws Exception {
		if (null != channelGroups) {
			channelGroups.add(e.getChannel());
		}
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
			throws Exception {
		RpcRequest request = (RpcRequest) ctx.getAttachment();
		if (e.getCause() instanceof ReadTimeoutException) {
			// The connection was OK but there was no traffic for last period.
			logger.warn("Disconnecting due to no inbound traffic");
		} else {
			logger.error("", e);
		}
		e.getChannel().close().awaitUninterruptibly();
	}

	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
		Object msg = e.getMessage();
		if (!(msg instanceof RpcRequest)) {
			logger.error("not RpcRequest received!");
			return;
		}
		RpcRequest request = (RpcRequest) msg;
		ctx.setAttachment(request);

		RpcResponse response = new RpcResponse(request.getRequestID());
		try {
			Object result = handle(request);
			response.setResult(result);
		} catch (Throwable t) {
			logger.error("handle rpc request fail! request:"+request, t);
			response.setException(t);
		}
		e.getChannel().write(response);
	}

	private Object handle(RpcRequest request) throws Throwable {
		String className = request.getClassName();
		Object rpcService = ExtensionLoader.getProxy(className);
		if (null == rpcService) {
			throw new NullPointerException("server interface config is null");
		}
		Method method = ReflectionCache.getMethod(request.getInterfaceName(), request.getMethodName(), request.getParameterTypes());
		Object[] parameters = request.getParameters();
		//invoke
		Object result = method.invoke(rpcService, parameters);
		return result;
	}

}