package org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.*; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.NAKACK2; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.GossipRouter; import org.jgroups.stack.Protocol; import org.jgroups.util.Promise; import org.jgroups.util.ResourceManager; import org.jgroups.util.StackType; import org.jgroups.util.Util; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Ensures that a disconnected channel reconnects correctly, for different * stack configurations. * * @author Ovidiu Feodorov <ovidiu@feodorov.com> * @author Bela Ban belaban@yahoo.com **/ @Test(groups={Global.STACK_INDEPENDENT, Global.GOSSIP_ROUTER},sequential=true) public class TUNNEL_Test extends ChannelTestBase{ protected JChannel channel, coordinator; protected final static String GROUP="TUNNEL_Test"; protected GossipRouter gossipRouter; protected int gossip_router_port; protected String gossip_router_hosts; protected InetAddress bind_addr; @BeforeClass void startRouter() throws Exception { String tmp=Util.getProperty(Global.BIND_ADDR); if(tmp == null) { StackType type=Util.getIpStackType(); tmp=type == StackType.IPv6? "::1" : "127.0.0.1"; } bind_addr=InetAddress.getByName(tmp); gossip_router_port=ResourceManager.getNextTcpPort(bind_addr); gossip_router_hosts=bind_addr.getHostAddress() + "[" + gossip_router_port + "]"; gossipRouter=new GossipRouter(gossip_router_port, null); gossipRouter.start(); } @AfterClass(alwaysRun=true) void stopRouter() throws Exception { gossipRouter.stop(); } @AfterMethod(alwaysRun=true) void tearDown() throws Exception { Util.close(channel, coordinator); } /** * Tests if the channel has a null local address after disconnect (using TUNNEL). **/ public void testNullLocalAddress_TUNNEL() throws Exception { channel=createTunnelChannel("A"); channel.connect(GROUP); assert channel.getAddress() != null; channel.disconnect(); assert channel.getAddress() == null; } /** * Tests connect-disconnect-connect sequence for a group with one member * (using default configuration). **/ public void testDisconnectConnectOne_Default() throws Exception { channel=createTunnelChannel("A"); channel.connect("DisconnectTest.testgroup-1"); channel.disconnect(); channel.connect("DisconnectTest.testgroup-2"); View view=channel.getView(); assert view.size() == 1; assert view.containsMember(channel.getAddress()); } /** * Tests connect-disconnect-connect sequence for a group with two members * (using default configuration). **/ public void testDisconnectConnectTwo_Default() throws Exception { coordinator=createTunnelChannel("B", false); channel=createTunnelChannel("A", false); coordinator.connect(GROUP); channel.connect("DisconnectTest.testgroup-1"); channel.disconnect(); channel.connect(GROUP); View view=channel.getView(); assert view.size() == 2; assert view.containsMember(channel.getAddress()); assert view.containsMember(coordinator.getAddress()); } /** * Tests connect-disconnect-connect-send sequence for a group with two * members, using the default stack configuration. Assumes that default * configuration includes pbcast.NAKACK. Test case introduced before fixing * pbcast.NAKACK bug, which used to leave pbcast.NAKACK in a broken state * after DISCONNECT. Because of this problem, the channel couldn't be used * to multicast messages. **/ public void testDisconnectConnectSendTwo_Default() throws Exception { final Promise<Message> msgPromise=new Promise<Message>(); coordinator=createTunnelChannel("B"); coordinator.connect(GROUP); coordinator.setReceiver(new PromisedMessageListener(msgPromise)); channel=createTunnelChannel("A"); channel.connect("DisconnectTest.testgroup-1"); channel.disconnect(); channel.connect(GROUP); channel.send(new Message(null, null, "payload")); Message msg=msgPromise.getResult(20000); assert msg != null; assert "payload".equals(msg.getObject()); } /** * Tests connect-disconnect-connect sequence for a group with one member * (using TUNNEL). **/ public void testDisconnectConnectOne_TUNNEL() throws Exception { channel=createTunnelChannel("A"); channel.connect("DisconnectTest.testgroup-1"); channel.disconnect(); channel.connect("DisconnectTest.testgroup-2"); View view=channel.getView(); assert view.size() == 1; assert view.containsMember(channel.getAddress()); } public void testFailureDetection() throws Exception { coordinator=createTunnelChannel("B"); coordinator.setName("coord"); coordinator.connect(GROUP); channel=createTunnelChannel("A"); channel.setName("participant"); channel.connect(GROUP); System.out.println("shutting down the participant channel"); Util.shutdown(channel); GMS coord_gms=(GMS)coordinator.getProtocolStack().findProtocol(GMS.class); if(coord_gms != null) coord_gms.setLevel("trace"); View view; long end_time=System.currentTimeMillis() + 20000; while(System.currentTimeMillis() < end_time) { view=coordinator.getView(); if(view.size() == 1) break; Util.sleep(500); } view=coordinator.getView(); assert view.size() == 1 : "coordinator's view is " + view + ", but we expected a view of 1 member"; if(coord_gms != null) coord_gms.setLevel("warn"); } public void testConnectThree() throws Exception { coordinator=createTunnelChannel("B"); channel=createTunnelChannel("A"); coordinator.connect(GROUP); channel.connect(GROUP); JChannel third = createTunnelChannel("C"); third.connect(GROUP); View view=channel.getView(); assert channel.getView().size() == 3; assert third.getView().size() == 3; assert view.containsMember(channel.getAddress()); assert view.containsMember(coordinator.getAddress()); Util.close(third); } /** * Tests connect-disconnect-connect sequence for a group with two members * (using TUNNEL). **/ public void testDisconnectConnectTwo_TUNNEL() throws Exception { coordinator=createTunnelChannel("B"); coordinator.connect(GROUP); channel=createTunnelChannel("A"); channel.connect("DisconnectTest.testgroup-1"); channel.disconnect(); channel.connect(GROUP); Thread.sleep(1000); View view=channel.getView(); assert view.size() == 2; assert view.containsMember(channel.getAddress()); assert view.containsMember(coordinator.getAddress()); } /** * Tests connect-disconnect-connect-send sequence for a group with two * members, using TUNNEL. Test case introduced before fixing pbcast.NAKACK * bug, which used to leave pbcast.NAKACK in a broken state after * DISCONNECT. Because of this problem, the channel couldn't be used to * multicast messages. **/ public void testDisconnectConnectSendTwo_TUNNEL() throws Exception { final Promise<Message> msgPromise=new Promise<Message>(); coordinator=createTunnelChannel("B"); coordinator.connect(GROUP); coordinator.setReceiver(new PromisedMessageListener(msgPromise)); channel=createTunnelChannel("A"); channel.connect("DisconnectTest.testgroup-1"); channel.disconnect(); channel.connect(GROUP); channel.send(new Message(null, null, "payload")); Message msg=msgPromise.getResult(20000); assert msg != null; assert "payload".equals(msg.getObject()); } protected JChannel createTunnelChannel(String name) throws Exception { return createTunnelChannel(name, true); } protected JChannel createTunnelChannel(String name, boolean include_failure_detection) throws Exception { TUNNEL tunnel=(TUNNEL)new TUNNEL().setValue("enable_bundling",false).setValue("bind_addr", bind_addr); tunnel.setGossipRouterHosts(gossip_router_hosts); List<Protocol> protocols=new ArrayList<Protocol>(); protocols.addAll(Arrays.asList(tunnel, new PING(), new MERGE2().setValue("min_interval", 1000).setValue("max_interval", 3000))); if(include_failure_detection) protocols.addAll(Arrays.asList(new FD().setValue("timeout", 2000).setValue("max_tries", 2), new VERIFY_SUSPECT())); protocols.addAll(Arrays.asList(new NAKACK2().setValue("use_mcast_xmit", false), new UNICAST(), new STABLE(), new GMS())); JChannel ch=new JChannel(protocols); if(name != null) ch.setName(name); return ch; } private static class PromisedMessageListener extends ReceiverAdapter { private final Promise<Message> promise; public PromisedMessageListener(Promise<Message> promise) { this.promise=promise; } public void receive(Message msg) { promise.setResult(msg); } } }