package org.jgroups.tests; import org.jgroups.*; import org.jgroups.conf.ConfiguratorFactory; import org.jgroups.conf.ProtocolConfiguration; import org.jgroups.conf.ProtocolStackConfigurator; import org.jgroups.protocols.BasicTCP; import org.jgroups.protocols.TCPPING; import org.jgroups.protocols.TP; import org.jgroups.protocols.UDP; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.ResourceManager; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import org.testng.AssertJUnit; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.net.InetAddress; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Tests which test the shared transport * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,sequential=true) public class SharedTransportTest extends ChannelTestBase { private JChannel a, b, c; private MyReceiver r1, r2, r3; static final String SINGLETON_1="singleton-1", SINGLETON_2="singleton-2"; @AfterMethod protected void tearDown() throws Exception { Util.close(c,b,a); r1=r2=r3=null; } public void testCreationNonSharedTransport() throws Exception { a=createChannel(true, 1, "A"); a.connect("SharedTransportTest.testCreationNonSharedTransport"); View view=a.getView(); System.out.println("view = " + view); assert view.size() == 1; } public void testCreationOfDuplicateCluster() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); // makeUnique(a, 2); b=createSharedChannel(SINGLETON_1, "B"); a.connect("x"); try { b.connect("x"); assert false : "b should not be able to join cluster 'x' as a has already joined it"; } catch(Exception ex) { System.out.println("b was not able to join the same cluster (\"x\") as expected"); } } public void testView() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); b=createSharedChannel(SINGLETON_2, "B"); a.setReceiver(new MyReceiver(SINGLETON_1)); b.setReceiver(new MyReceiver(SINGLETON_2)); a.connect("x"); b.connect("x"); Util.waitUntilAllChannelsHaveSameSize(30000, 1000, a,b); } public void testView2() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); b=createSharedChannel(SINGLETON_1, "B"); a.setReceiver(new MyReceiver("first-channel")); b.setReceiver(new MyReceiver("second-channel")); a.connect("x"); b.connect("y"); View view=a.getView(); assert view.size() == 1; view=b.getView(); assert view.size() == 1; } /** * Tests http://jira.jboss.com/jira/browse/JGRP-689: with TCP or UDP.ip_mcast=false, the transport iterates through * the 'members' instance variable to send a group message. However, the 'members' var is the value of the last * view change received. If we receive multiple view changes, this leads to incorrect membership. * @throws Exception */ public void testView3() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); b=createSharedChannel(SINGLETON_1, "B"); c=createSharedChannel(SINGLETON_2, "C"); r1=new MyReceiver("A::" + SINGLETON_1); r2=new MyReceiver("B::" + SINGLETON_1); r3=new MyReceiver("C::" + SINGLETON_2); a.setReceiver(r1); b.setReceiver(r2); c.setReceiver(r3); a.connect("cluster-1"); c.connect("cluster-1"); Util.waitUntilAllChannelsHaveSameSize(30000, 1000, a,c); a.send(new Message(null, null, "msg-1")); c.send(new Message(null, null, "msg-2")); // async sending - wait until the message has been received by A and C for(int i=0; i < 20; i++) { if(r1.getList().size() == 2 && r3.getList().size() == 3) break; Util.sleep(500); } List<Message> list=r1.getList(); assert list.size() == 2; list=r3.getList(); assert list.size() == 2; r1.clear(); r2.clear(); r3.clear(); b.connect("cluster-2"); a.send(new Message(null, null, "msg-3")); b.send(new Message(null, null, "msg-4")); c.send(new Message(null, null, "msg-5")); Util.sleep(1000); // async sending - wait a little // printLists(r1, r2, r3); list=r1.getList(); assert list.size() == 2; list=r2.getList(); assert list.size() == 1; list=r3.getList(); assert list.size() == 2; } public void testView4() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); r1=new MyReceiver("A::" + SINGLETON_1); a.setReceiver(r1); a.connect("cluster-X"); a.send(new Message(null, null, "msg-1")); Util.sleep(1000); // async sending - wait a little List<Message> list=r1.getList(); assert list.size() == 1; a.send(new Message(null, null, "msg-2")); a.send(new Message(null, null, "msg-3")); a.send(new Message(null, null, "msg-4")); Util.sleep(1000); // async sending - wait a little list=r1.getList(); assert list.size() == 4; } public void testSharedTransportAndNonsharedTransport() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); b=createChannel(); a.setReceiver(new MyReceiver("first-channel")); b.setReceiver(new MyReceiver("second-channel")); a.connect("x"); b.connect("x"); Util.waitUntilAllChannelsHaveSameSize(30000, 1000, a,b); } public void testCreationOfDifferentCluster() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); b=createSharedChannel(SINGLETON_2, "B"); a.connect("x"); b.connect("x"); Util.waitUntilAllChannelsHaveSameSize(30000, 1000, a,b); } public void testReferenceCounting() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); r1=new MyReceiver("a"); a.setReceiver(r1); b=createSharedChannel(SINGLETON_1, "B"); r2=new MyReceiver("b"); b.setReceiver(r2); c=createSharedChannel(SINGLETON_1, "C"); r3=new MyReceiver("c"); c.setReceiver(r3); a.connect("A"); b.connect("B"); c.connect("C"); a.send(null, "message from a"); b.send(null, "message from b"); c.send(null, "message from c"); Util.sleep(500); assert r1.size() == 1; assert r2.size() == 1; assert r3.size() == 1; r1.clear(); r2.clear(); r3.clear(); b.disconnect(); System.out.println("\n"); a.send(null, "message from a"); c.send(null, "message from c"); Util.sleep(500); assert r1.size() == 1 : "size should be 1 but is " + r1.size(); assert r3.size() == 1 : "size should be 1 but is " + r3.size(); r1.clear(); r3.clear(); c.disconnect(); System.out.println("\n"); a.send(null, "message from a"); Util.sleep(500); assert r1.size() == 1; } /** * Tests that a second channel with the same group name can be * created and connected once the first channel is disconnected. * @throws Exception */ public void testSimpleReCreation() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); a.setReceiver(new MyReceiver("A")); a.connect("A"); a.disconnect(); b=createSharedChannel(SINGLETON_1, "B"); b.setReceiver(new MyReceiver("A'")); b.connect("A"); } /** * Tests that a second channel with the same group name can be * created and connected once the first channel is disconnected even * if 3rd channel with a different group name is still using the shared * transport. * @throws Exception */ public void testCreationFollowedByDeletion() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); a.setReceiver(new MyReceiver("A")); a.connect("A"); b=createSharedChannel(SINGLETON_1, "B"); b.setReceiver(new MyReceiver("B")); b.connect("B"); b.close(); a.close(); } public void test2ChannelsCreationFollowedByDeletion() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); a.setReceiver(new MyReceiver("A")); a.connect("A"); b=createSharedChannel(SINGLETON_2, "B"); b.setReceiver(new MyReceiver("B")); b.connect("A"); c=createSharedChannel(SINGLETON_2, "C"); c.setReceiver(new MyReceiver("C")); c.connect("B"); c.send(null, "hello world from C"); } /** * Test case for https://issues.jboss.org/browse/JGRP-1356 * @throws Exception */ public void testFailedFirstChannel() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); TP transport=a.getProtocolStack().getTransport(); transport.setBindPort(128); // set the bind_port to an incorrect value (< 1024), this will fail on connect() a.setReceiver(new MyReceiver("A")); try { a.connect("A"); } catch(Exception ex) { System.out.println("caught exception - as expected: " + ex); } b=createSharedChannel(SINGLETON_1, "B"); b.setReceiver(new MyReceiver("B")); try { b.connect("B"); } catch(Exception ex) { System.out.println("caught exception - as expected: " + ex); } transport.setBindPort(0); // fix the problem by picking an ephemeral port > 1024 b.connect("B"); } public void testReCreationWithSurvivingChannel() throws Exception { // Create 2 channels sharing a transport System.out.println("-- creating A"); a=createSharedChannel(SINGLETON_1, "A"); a.setReceiver(new MyReceiver("A")); a.connect("A"); System.out.println("-- creating B"); b=createSharedChannel(SINGLETON_1, "B"); b.setReceiver(new MyReceiver("B")); b.connect("B"); System.out.println("-- disconnecting A"); a.disconnect(); // a is disconnected so we should be able to create a new channel with group "A" System.out.println("-- creating A'"); c=createSharedChannel(SINGLETON_1, "C"); c.setReceiver(new MyReceiver("A'")); c.connect("A"); } /** * Tests http://jira.jboss.com/jira/browse/JGRP-737 * @throws Exception */ public void testShutdownOfTimer() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); b=createSharedChannel(SINGLETON_1, "B"); a.connect("x"); b.connect("y"); TimeScheduler timer1=a.getProtocolStack().getTransport().getTimer(); TimeScheduler timer2=b.getProtocolStack().getTransport().getTimer(); assert timer1 == timer2; assert !timer1.isShutdown(); assert !timer2.isShutdown(); Util.sleep(500); b.close(); assert !timer2.isShutdown(); assert !timer1.isShutdown(); a.close(); // now, reference counting reaches 0, so the timer thread pool is stopped assert timer2.isShutdown(); assert timer1.isShutdown(); } /** Create channels A, B and C. Close A. This will close the timer and transports threads (!), so B will * not be able to send messages anymore, so C will not receive any messages * Tests http://jira.jboss.com/jira/browse/JGRP-737 */ public void testSendingOfMessagesAfterChannelClose() throws Exception { MyReceiver rec_a=new MyReceiver("A"), rec_b=new MyReceiver("B"), rec_c=new MyReceiver("C"); a=createSharedChannel(SINGLETON_1, "A"); a.setName("A"); a.setReceiver(rec_a); a.connect("one"); b=createSharedChannel(SINGLETON_1, "B"); b.setName("B"); b.setReceiver(rec_b); b.connect("two"); c=createSharedChannel(SINGLETON_2, "C"); c.setName("C"); c.setReceiver(rec_c); c.connect("two"); b.send(null, "first"); // msg delivery is asynchronous, so give members some time to receive the msg (incl retransmission) for(int i=0; i < 20; i++) { if(rec_b.size() == 1 && rec_c.size() == 1 && rec_a.size() == 0) break; Util.sleep(500); } assertSize(1, rec_b, rec_c); assertSize(0, rec_a); a.close(); b.send(null, "second"); Util.sleep(500); assertSize(0, rec_a); assertSize(2, rec_b, rec_c); } /** * Use a CountDownLatch to concurrently connect 3 channels; confirms * the channels connect * * @throws Exception */ public void testConcurrentCreation() throws Exception { a=createSharedChannel(SINGLETON_1, "A"); r1=new MyReceiver("a"); a.setReceiver(r1); b=createSharedChannel(SINGLETON_1, "B"); r2=new MyReceiver("b"); b.setReceiver(r2); c=createSharedChannel(SINGLETON_1, "C"); r3=new MyReceiver("c"); c.setReceiver(r3); CountDownLatch startLatch = new CountDownLatch(1); CountDownLatch finishLatch = new CountDownLatch(3); ConnectTask connectA = new ConnectTask(a, "a", startLatch, finishLatch); Thread threadA = new Thread(connectA); threadA.setDaemon(true); threadA.start(); ConnectTask connectB = new ConnectTask(b, "b", startLatch, finishLatch); Thread threadB = new Thread(connectB); threadB.setDaemon(true); threadB.start(); ConnectTask connectC = new ConnectTask(c, "c", startLatch, finishLatch); Thread threadC = new Thread(connectC); threadC.setDaemon(true); threadC.start(); startLatch.countDown(); try { boolean finished = finishLatch.await(20, TimeUnit.SECONDS); if (connectA.exception != null) { AssertJUnit.fail("connectA threw exception " + connectA.exception); } if (connectB.exception != null) { AssertJUnit.fail("connectB threw exception " + connectB.exception); } if (connectC.exception != null) { AssertJUnit.fail("connectC threw exception " + connectC.exception); } if (!finished) { if (threadA.isAlive()) AssertJUnit.fail("threadA did not finish"); if (threadB.isAlive()) AssertJUnit.fail("threadB did not finish"); if (threadC.isAlive()) AssertJUnit.fail("threadC did not finish"); } } finally { if (threadA.isAlive()) threadA.interrupt(); if (threadB.isAlive()) threadB.interrupt(); if (threadC.isAlive()) threadC.interrupt(); } } private static void assertSize(int expected, MyReceiver... receivers) { for(MyReceiver recv: receivers) { assertEquals(expected, recv.size()); } } private JChannel createSharedChannel(String singleton_name, String name) throws Exception { ProtocolStackConfigurator config=ConfiguratorFactory.getStackConfigurator(channel_conf); List<ProtocolConfiguration> protocols=config.getProtocolStack(); ProtocolConfiguration transport=protocols.get(0); transport.getProperties().put(Global.SINGLETON_NAME, singleton_name); JChannel ch=new JChannel(config); if(name != null) ch.setName(name); return ch; } protected static void makeUnique(Channel channel, int num) throws Exception { ProtocolStack stack=channel.getProtocolStack(); TP transport=stack.getTransport(); InetAddress bind_addr=transport.getBindAddress(); if(transport instanceof UDP) { String mcast_addr=ResourceManager.getNextMulticastAddress(); short mcast_port=ResourceManager.getNextMulticastPort(bind_addr); ((UDP)transport).setMulticastAddress(InetAddress.getByName(mcast_addr)); ((UDP)transport).setMulticastPort(mcast_port); } else if(transport instanceof BasicTCP) { List<Short> ports=ResourceManager.getNextTcpPorts(bind_addr, num); transport.setBindPort(ports.get(0)); transport.setPortRange(num); Protocol ping=stack.findProtocol(TCPPING.class); if(ping == null) throw new IllegalStateException("TCP stack must consist of TCP:TCPPING - other config are not supported"); List<String> initial_hosts=new LinkedList<String>(); for(short port: ports) { initial_hosts.add(bind_addr + "[" + port + "]"); } String tmp=Util.printListWithDelimiter(initial_hosts, ","); List<IpAddress> init_hosts = Util.parseCommaDelimitedHosts(tmp, 1) ; ((TCPPING)ping).setInitialHosts(init_hosts) ; } else { throw new IllegalStateException("Only UDP and TCP are supported as transport protocols"); } } private static class MyReceiver extends ReceiverAdapter { final List<Message> list=new LinkedList<Message>(); final String name; private MyReceiver(String name) { this.name=name; } public List<Message> getList() { return list; } public int size() { return list.size(); } public void clear() { list.clear(); } public void receive(Message msg) { System.out.println("[" + name + "]: received message from " + msg.getSrc() + ": " + msg.getObject()); list.add(msg); } public void viewAccepted(View new_view) { StringBuilder sb=new StringBuilder(); sb.append("[" + name + "]: view = " + new_view); System.out.println(sb); } public String toString() { return super.toString() + " (size=" + list.size() + ")"; } } private static class ConnectTask implements Runnable { private final Channel channel; private final String clusterName; private final CountDownLatch startLatch; private final CountDownLatch finishLatch; private Exception exception; ConnectTask(Channel channel, String clusterName, CountDownLatch startLatch, CountDownLatch finishLatch) { this.channel = channel; this.clusterName = clusterName; this.startLatch = startLatch; this.finishLatch = finishLatch; } public void run() { try { startLatch.await(); channel.connect(clusterName); } catch (Exception e) { e.printStackTrace(System.out); this.exception = e; } finally { finishLatch.countDown(); } } } }