package ddth.dasp.servlet.netty.api;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
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.handler.codec.frame.TooLongFrameException;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.handler.codec.http.QueryStringDecoder;
import org.jboss.netty.handler.timeout.IdleState;
import org.jboss.netty.handler.timeout.IdleStateEvent;
import org.jboss.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ddth.dasp.common.DaspGlobal;
import ddth.dasp.common.RequestLocal;
import ddth.dasp.common.utils.ApiUtils;
import ddth.dasp.common.utils.JsonUtils;
import ddth.dasp.servlet.netty.AbstractHttpHandler;
public class JsonApiHandler extends AbstractHttpHandler {
private final static String URI_PREFIX = "/api";
private static Logger LOGGER = LoggerFactory.getLogger(JsonApiHandler.class);
private ChannelFuture responseError(Channel channel, HttpResponseStatus status, String message) {
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);
response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "application/json; charset=UTF-8");
response.setContent(ChannelBuffers.copiedBuffer(message, CharsetUtil.UTF_8));
ChannelFuture future = channel.write(response);
return future;
}
@SuppressWarnings("unchecked")
protected Object parseInput(HttpRequest request, String uri) {
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(uri);
String jsonEncodedInput = request.getContent().toString(CharsetUtil.UTF_8);
Object params = null;
try {
params = JsonUtils.fromJson(jsonEncodedInput);
} catch (Exception e) {
//
}
if (params == null) {
params = new HashMap<String, Object>();
}
if (params instanceof Map<?, ?>) {
Map<String, List<String>> urlParams = queryStringDecoder.getParameters();
for (Entry<String, List<String>> entry : urlParams.entrySet()) {
String key = entry.getKey();
List<String> values = entry.getValue();
for (String value : values) {
((Map<String, Object>) params).put(key, value);
}
}
}
return params;
}
private ChannelFuture responseApiCall(HttpRequest request, Channel channel, String uri) {
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(uri);
String[] tokens = queryStringDecoder.getPath().replaceAll("^\\/+", "")
.replaceAll("\\/+$", "").split("\\/");
String moduleName = tokens.length > 0 ? tokens[0] : null;
String functionName = tokens.length > 1 ? tokens[1] : null;
String authKey = tokens.length > 2 ? tokens[2] : null;
Object apiParams = parseInput(request, uri);
String remoteAddr = channel.getRemoteAddress().toString();
Object result = ApiUtils.executeApi(moduleName, functionName, apiParams, authKey,
remoteAddr);
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "application/json; charset=UTF-8");
response.setContent(ChannelBuffers.copiedBuffer(JsonUtils.toJson(result), CharsetUtil.UTF_8));
ChannelFuture future = channel.write(response);
return future;
}
/**
* {@inheritDoc}
*/
@Override
protected void handleRequest(HttpRequest request, Channel userChannel) throws Exception {
// init the request local and bound it to the current thread if needed.
RequestLocal oldRequestLocal = RequestLocal.get();
RequestLocal.set(new RequestLocal());
try {
String uri = request.getUri();
String contextPath = DaspGlobal.getServletContext().getContextPath();
if (uri.startsWith(contextPath)) {
uri = uri.substring(contextPath.length());
}
ChannelFuture future;
if (!uri.startsWith(URI_PREFIX)) {
future = responseError(userChannel, HttpResponseStatus.BAD_REQUEST,
"Request must starts with '/api'!");
} else {
future = responseApiCall(request, userChannel, uri.substring(URI_PREFIX.length()));
}
future.addListener(ChannelFutureListener.CLOSE);
} finally {
RequestLocal.set(oldRequestLocal);
}
}
@Override
public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e) {
IdleState state = e.getState();
if (state == IdleState.READER_IDLE || state == IdleState.WRITER_IDLE) {
long duration = System.currentTimeMillis() - e.getLastActivityTimeMillis();
String msg = (state == IdleState.READER_IDLE ? "Read timeout (" : "Write timeout (")
+ duration + " ms)!";
msg = e.getChannel() + ": " + msg;
LOGGER.warn(msg);
if (state == IdleState.READER_IDLE) {
e.getChannel().close();
e.getFuture().cancel();
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
Throwable t = e.getCause();
if (t instanceof TooLongFrameException) {
Channel channel = e.getChannel();
String msg = channel.toString() + "/" + t.getMessage();
LOGGER.error(msg, t);
} else {
LOGGER.error(t.getMessage(), t);
}
e.getChannel().close();
e.getFuture().cancel();
}
}