package net.jxta.impl.endpoint.netty; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import net.jxta.endpoint.EndpointAddress; import net.jxta.endpoint.EndpointService; import net.jxta.endpoint.MessageSender; import net.jxta.endpoint.Messenger; import net.jxta.endpoint.MessengerEventListener; import net.jxta.logging.Logger; import net.jxta.logging.Logging; import net.jxta.peer.PeerID; import net.jxta.peergroup.PeerGroup; import net.jxta.peergroup.PeerGroupID; import org.jboss.netty.bootstrap.ClientBootstrap; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFactory; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.group.ChannelGroup; import org.jboss.netty.channel.group.ChannelGroupFuture; import org.jboss.netty.channel.group.DefaultChannelGroup; import org.jboss.netty.util.HashedWheelTimer; /** * Client side of a netty based transport. Responsible for initiating outgoing connections. * * @author Iain McGinniss (iain.mcginniss@onedrum.com) */ public class NettyTransportClient implements MessageSender, TransportClientComponent { private final class ClientConnectionRegistrationHandler implements NettyChannelRegistry { public EndpointAddress directedAt; public EndpointAddress logicalEndpointAddress; public CountDownLatch latch = new CountDownLatch(1); public void newConnection(Channel channel, EndpointAddress directedAt, EndpointAddress logicalEndpointAddress) { this.directedAt = directedAt; this.logicalEndpointAddress = logicalEndpointAddress; latch.countDown(); } } private static final Logger LOG = Logging.getLogger(NettyTransportClient.class.getName()); private EndpointAddress localAddress; private PeerGroup group; private PeerGroupID homeGroupID; private PeerID localPeerID; private EndpointService endpointService; private MessengerEventListener messageEventListener; private AddressTranslator addrTranslator; private ChannelGroup channels; private HashedWheelTimer timeoutTimer; private ChannelGroupFuture closeChannelsFuture; private AtomicBoolean started; private AtomicBoolean stopping; private ChannelFactory clientFactory; private EndpointAddress returnAddress; public NettyTransportClient(ChannelFactory clientFactory, AddressTranslator addrTranslator, PeerGroup group, EndpointAddress returnAddress) { this.started = new AtomicBoolean(false); this.stopping = new AtomicBoolean(false); this.channels = new DefaultChannelGroup(); this.addrTranslator = addrTranslator; this.clientFactory = clientFactory; this.returnAddress = returnAddress; localAddress = returnAddress; this.group = group; this.homeGroupID = group.getPeerGroupID(); this.localPeerID = group.getPeerID(); timeoutTimer = new HashedWheelTimer(); } public boolean start(EndpointService endpointService) { this.endpointService = endpointService; messageEventListener = endpointService.addMessageTransport(this); if(messageEventListener == null) { return false; } started.set(true); return true; } public void beginStop() { if(!started.get()) { Logging.logCheckedWarning(LOG, "Netty transport server for protocol ", addrTranslator.getProtocolName(), " already stopped or never started!"); return; } closeChannelsFuture = channels.close(); stopping.set(true); } public void stop() { if(!stopping.get()) { Logging.logCheckedWarning(LOG, "Netty transport server for protocol ", addrTranslator.getProtocolName(), " already stopped or never started!"); return; } closeChannelsFuture.awaitUninterruptibly(); clientFactory.releaseExternalResources(); timeoutTimer.stop(); endpointService.removeMessageTransport(this); endpointService = null; } /** * {@inheritDoc } * @param dest * @param hint * @return */ public Messenger getMessenger(EndpointAddress dest) { // public Messenger getMessenger(EndpointAddress dest, Object hint) { if(!started.get()) { Logging.logCheckedWarning(LOG, "Request to get messenger for ", dest.toString(), " when netty transport client stopped or never started"); return null; } Logging.logCheckedInfo(LOG, "processing request to open connection to ", dest); ClientConnectionRegistrationHandler clientRegistry = new ClientConnectionRegistrationHandler(); ClientBootstrap bootstrap = new ClientBootstrap(clientFactory); bootstrap.setPipelineFactory(new NettyTransportChannelPipelineFactory(group, localPeerID, timeoutTimer, clientRegistry, addrTranslator, dest, returnAddress)); ChannelFuture connectFuture = bootstrap.connect(addrTranslator.toSocketAddress(dest)); try { if(!connectFuture.await(5000L, TimeUnit.MILLISECONDS)) { if(Logging.SHOW_INFO && LOG.isInfoEnabled()) { LOG.infoParams("Netty transport for protocol {} failed to connect to {} within acceptable time", new Object[] { addrTranslator.getProtocolName(), dest }); } return null; } } catch(InterruptedException e) { Logging.logCheckedWarning(LOG, "Interrupted while waiting for connection to ", dest, " to be established"); connectFuture.cancel(); return null; } if(!connectFuture.isSuccess()) { if(Logging.SHOW_INFO && LOG.isInfoEnabled()) { Throwable cause = connectFuture.getCause(); String causeString = (cause != null) ? cause.getMessage() : "cause unknown"; String message = String.format("Netty transport for protocol %s failed to connect to %s - %s", addrTranslator.getProtocolName(), dest, causeString); LOG.info(message); } return null; } boolean established = false; try { established = clientRegistry.latch.await(15L, TimeUnit.SECONDS); } catch(InterruptedException e) { Logging.logCheckedWarning(LOG, "Interrupted while waiting for connection handover\n", e); } if(!established) { Logging.logCheckedWarning(LOG, "Connection handover timed out - either remote host was not a valid JXTA peer or did not respond on time"); connectFuture.getChannel().close(); return null; } if(Logging.SHOW_INFO && LOG.isInfoEnabled()) { LOG.infoParams("succeeded in connecting to {}, remote peer has logical address {}", new Object[] { dest, clientRegistry.logicalEndpointAddress }); } channels.add(connectFuture.getChannel()); // return new NettyMessenger(connectFuture.getChannel(), homeGroupID, localPeerID, clientRegistry.directedAt, clientRegistry.logicalEndpointAddress, endpointService); return new AsynchronousNettyMessenger(connectFuture.getChannel(), homeGroupID, localPeerID, clientRegistry.directedAt, clientRegistry.logicalEndpointAddress, endpointService); } public boolean allowsRouting() { return true; } public EndpointAddress getPublicAddress() { return localAddress; } public boolean isConnectionOriented() { return true; } @Deprecated public boolean ping(EndpointAddress addr) { throw new RuntimeException("ping is deprecated, do not use!"); } public EndpointService getEndpointService() { return endpointService; } public String getProtocolName() { return addrTranslator.getProtocolName(); } }