package com.indyforge.foxnet.rmi.transport.network.handler.reqres;
import java.nio.channels.ClosedChannelException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Logger;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandler.Sharable;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import com.indyforge.foxnet.rmi.util.Future;
import com.indyforge.foxnet.rmi.util.FutureCallback;
import com.indyforge.foxnet.rmi.util.Request;
@Sharable
public final class ReqResHandler extends SimpleChannelHandler {
public static final ReqResHandler INSTANCE = new ReqResHandler();
// The logger
protected final Logger logger = Logger.getLogger(getClass().getName());
@SuppressWarnings("unchecked")
private static void cancelRequests(ChannelHandlerContext ctx) {
// Lookup the queue
ConcurrentMap<Long, Request> requests = (ConcurrentMap<Long, Request>) ctx
.getAttachment();
// Finish the remaining requests
for (Object request : requests.values().toArray()) {
((Request) request).fail(new ClosedChannelException());
}
}
/*
* (non-Javadoc)
*
* @see
* org.jboss.netty.channel.SimpleChannelHandler#channelOpen(org.jboss.netty
* .channel.ChannelHandlerContext,
* org.jboss.netty.channel.ChannelStateEvent)
*/
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
// Create a new queue at beginning
ctx.setAttachment(new ConcurrentHashMap<Long, Request>());
super.channelOpen(ctx, e);
}
/*
* (non-Javadoc)
*
* @see
* org.jboss.netty.channel.SimpleChannelHandler#channelClosed(org.jboss.
* netty.channel.ChannelHandlerContext,
* org.jboss.netty.channel.ChannelStateEvent)
*/
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
// Cancel the requests
cancelRequests(ctx);
super.channelClosed(ctx, e);
}
@SuppressWarnings("unchecked")
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
// If reqres message...
if (e.getMessage() instanceof ReqResMessage) {
ReqResMessage reqResMessage = (ReqResMessage) e.getMessage();
if (reqResMessage.isRequest()) {
// Create new request
Request request = new Request(reqResMessage.getData(),
reqResMessage.getId());
// Get final channel
final Channel channel = ctx.getChannel();
// Send result back when completed
request.add(new FutureCallback() {
@Override
public void completed(Future future) throws Exception {
// Convert
Request request = (Request) future;
// Just write the response
channel.write(new ReqResMessage(request.attachment(),
request.cause(), request.id(), false));
}
});
// Send upstream
Channels.fireMessageReceived(ctx, request);
} else {
// Lookup requests
ConcurrentMap<Long, Request> requests = (ConcurrentMap<Long, Request>) ctx
.getAttachment();
// Try to find the correct request
Request request = requests.remove(reqResMessage.getId());
if (request == null) {
logger.warning("Response message received but "
+ "the id=\"" + reqResMessage.getId()
+ "\" does not represent a request");
} else {
// Complete the request
request.complete(reqResMessage.getData(),
reqResMessage.getCause());
}
}
} else {
// Not for us...
super.messageReceived(ctx, e);
}
}
@SuppressWarnings("unchecked")
@Override
public void writeRequested(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
// Are we trying to send a request ?
if (e.getMessage() instanceof Request) {
// Convert
final Request request = (Request) e.getMessage();
// Lookup requests
final ConcurrentMap<Long, Request> requests = (ConcurrentMap<Long, Request>) ctx
.getAttachment();
// Try to save the request or fail it
if (requests.putIfAbsent(request.id(), request) != null) {
request.fail(new IllegalStateException("The request "
+ "id is already used"));
} else {
// Remove the request when finished
request.add(new FutureCallback() {
@Override
public void completed(Future future) throws Exception {
requests.remove(((Request) future).id());
}
});
// Hook
e.getFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future)
throws Exception {
/*
* If the write process was not successful there will
* never be a response.
*/
if (!future.isSuccess()) {
request.fail(future.getCause());
}
}
});
// Proceed the write request
Channels.write(ctx, e.getFuture(), new ReqResMessage(request));
}
} else {
// Not for us...
super.writeRequested(ctx, e);
}
}
}