package net.jxta.impl.endpoint.netty;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.jxta.endpoint.EndpointAddress;
import net.jxta.endpoint.EndpointService;
import net.jxta.endpoint.Message;
import net.jxta.endpoint.MessageElement;
import net.jxta.impl.endpoint.AsynchronousMessenger;
import net.jxta.impl.endpoint.EndpointServiceImpl;
import net.jxta.impl.endpoint.QueuedMessage;
import net.jxta.logging.Logging;
import net.jxta.peer.PeerID;
import net.jxta.peergroup.PeerGroupID;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
/**
* Netty channel based messenger implementation. Unfortunately, this extends BlockingMessenger despite
* the elegant non-blocking, event driven architecture of Netty underneath. This was primarily done
* for haste rather than for any overriding architectural reason, though there are some areas of the
* endpoint service code that expect blocking messengers explicitly.
*
* @author iain.mcginniss@onedrum.com
*/
public class AsynchronousNettyMessenger extends AsynchronousMessenger implements MessageArrivalListener {
private static final Logger LOG = Logger.getLogger(NettyMessenger.class.getName());
private static final int QUEUE_SIZE = Integer.getInteger("net.jxta.impl.endpoint.async.queuesize", 100);
private Channel channel;
private EndpointAddress logicalDestinationAddr;
private EndpointService endpointService;
private PeerID localPeerId;
private EndpointAddress localAddress;
public AsynchronousNettyMessenger(Channel channel, PeerGroupID homeGroupID, PeerID localPeerID, EndpointAddress localAddress, EndpointAddress logicalDestinationAddress, EndpointService endpointService) {
super(homeGroupID, localAddress, QUEUE_SIZE);
this.channel = channel;
this.localPeerId = localPeerID;
this.localAddress = new EndpointAddress(localPeerId, null, null);
this.endpointService = endpointService;
this.logicalDestinationAddr = logicalDestinationAddress;
attachMessageListener();
}
private void attachMessageListener() {
MessageDispatchHandler handler = (MessageDispatchHandler)channel.getPipeline().get(MessageDispatchHandler.NAME);
handler.setMessageArrivalListener(this);
}
@Override
protected void requestClose() {
LOG.log(Level.FINE, "Closing netty channel for messenger to {0}", logicalDestinationAddr);
if(channel.isOpen()) {
channel.close();
}
}
@Override
public boolean isClosed() {
return super.isClosed() || !channel.isOpen();
}
@Override
protected boolean sendMessageImpl(final QueuedMessage message) {
if (isClosed()) {
IOException cause = new IOException("Messenger was closed, it cannot be used to send messages.");
message.getWriteListener().writeFailure(cause);
if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, cause.getMessage(), cause);
}
return false;
}
if(channel.isWritable()) {
writeMessage(message);
return true;
} else {
return false;
}
}
private void writeMessage(final QueuedMessage message) {
ChannelFuture future = channel.write(message.getMessage());
message.getWriteListener().writeSubmitted();
final ChannelFutureListener channelFutureListener = new ChannelFutureListener()
{
public void operationComplete(ChannelFuture future) throws Exception
{
if (!future.isSuccess())
{
message.getWriteListener().writeFailure(future.getCause());
}
else
{
message.getWriteListener().writeSuccess();
}
}
};
future.addListener(channelFutureListener);
}
public void messageArrived(final Message msg) {
// Extract the source and destination
final EndpointAddress srcAddr
= extractEndpointAddress(msg,
EndpointServiceImpl.MESSAGE_SOURCE_NS,
EndpointServiceImpl.MESSAGE_SOURCE_NAME,
"source");
final EndpointAddress dstAddr
= extractEndpointAddress(msg,
EndpointServiceImpl.MESSAGE_DESTINATION_NS,
EndpointServiceImpl.MESSAGE_DESTINATION_NAME,
"destination");
if(srcAddr == null || isLoopback(srcAddr) || dstAddr == null) {
return;
}
ExecutorService executorService = endpointService.getGroup().getTaskManager().getExecutorService();
executorService.execute(new Runnable() {
public void run() {
endpointService.processIncomingMessage(msg, srcAddr, dstAddr);
}
});
}
public void connectionDied() {
LOG.log(Level.INFO, "Underlying channel for messenger to {0} has died", logicalDestinationAddr);
connectionFailed();
}
public final void connectionDisposed() {
connectionCloseComplete();
}
public void channelSaturated(boolean saturated) {
if(saturated == false) {
pullMessages();
}
}
@Override
protected EndpointAddress getLocalAddress() {
return localAddress;
}
@Override
public EndpointAddress getLogicalDestinationAddress() {
return logicalDestinationAddr;
}
private boolean isLoopback(EndpointAddress srcAddr) {
if (localAddress.equals(srcAddr)) {
if (Logging.SHOW_DEBUG && LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "Loopback message detected");
}
return true;
}
return false;
}
private EndpointAddress extractEndpointAddress(Message msg, String elementNamespace, String elementName, String addrType) {
MessageElement element = msg.getMessageElement(elementNamespace, elementName);
if(element == null) {
if (Logging.SHOW_DEBUG && LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "Message with no " + addrType + " address detected: " + msg);
}
return null;
} else {
msg.removeMessageElement(element);
return new EndpointAddress(element.toString());
}
}
}