package org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.DUPL; import org.jgroups.protocols.pbcast.NAKACK2; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Tuple; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; /** * Tests whether UNICAST or NAKACK prevent delivery of duplicate messages. JGroups guarantees that a message is * delivered once and only once. The test inserts DUPL below both UNICAST and NAKACK and makes it duplicate (1) * unicast, (2) multicast, (3) regular and (4) OOB messages. The receiver(s) then check for the presence of duplicate * messages. * @author Bela Ban */ @Test(groups=Global.STACK_DEPENDENT,singleThreaded=true) public class DuplicateTest extends ChannelTestBase { private JChannel a, b, c; protected Address a1, a2, a3; private MyReceiver r1, r2, r3; @BeforeMethod void init() throws Exception { createChannels(true, true, (short)5, (short)5); a1=a.getAddress(); a2=b.getAddress(); a3=c.getAddress(); r1=new MyReceiver("A"); r2=new MyReceiver("B"); r3=new MyReceiver("C"); a.setReceiver(r1); b.setReceiver(r2); c.setReceiver(r3); } @AfterMethod void tearDown() throws Exception { removeDUPL(c,b,a); Util.close(c,b,a); } public void testRegularUnicastsToSelf() throws Exception { send(a, a.getAddress(), false, 10); sendStableMessages(a,b, c); check(r1, 1, false, new Tuple<>(a1, 10)); } public void testOOBUnicastsToSelf() throws Exception { send(a, a.getAddress(), true, 10); sendStableMessages(a,b,c); check(r1, 1, true, new Tuple<>(a1, 10)); } public void testRegularUnicastsToOthers() throws Exception { send(a, b.getAddress(), false, 10); send(a, c.getAddress(), false, 10); sendStableMessages(a,b,c); check(r2, 1, false, new Tuple<>(a1, 10)); check(r3, 1, false, new Tuple<>(a1, 10)); } @Test(invocationCount=10) public void testOOBUnicastsToOthers() throws Exception { send(a, b.getAddress(), true, 10); send(a, c.getAddress(), true, 10); sendStableMessages(a,b,c); check(r2, 1, true, new Tuple<>(a1, 10)); check(r3, 1, true, new Tuple<>(a1, 10)); } public void testRegularMulticastToAll() throws Exception { send(a, null, false, 10); sendStableMessages(a,b,c); check(r1, 1, false, new Tuple<>(a1, 10)); check(r2, 1, false, new Tuple<>(a1, 10)); check(r3, 1, false, new Tuple<>(a1, 10)); } public void testOOBMulticastToAll() throws Exception { send(a, null, true, 10); sendStableMessages(a,b,c); check(r1,1,true,new Tuple<>(a1,10)); check(r2, 1, true, new Tuple<>(a1, 10)); check(r3, 1, true, new Tuple<>(a1, 10)); } public void testRegularMulticastToAll3Senders() throws Exception { send(a, null, false, 10); send(b, null, false, 10); send(c, null, false, 10); sendStableMessages(a,b,c); check(r1, 3, false, new Tuple<>(a1, 10), new Tuple<>(a2, 10), new Tuple<>(a3, 10)); check(r2, 3, false, new Tuple<>(a1, 10), new Tuple<>(a2, 10), new Tuple<>(a3, 10)); check(r3, 3, false, new Tuple<>(a1, 10), new Tuple<>(a2, 10), new Tuple<>(a3, 10)); } @Test(invocationCount=5) public void testOOBMulticastToAll3Senders() throws Exception { send(a, null, true, 10); send(b, null, true, 10); send(c, null, true, 10); sendStableMessages(a,b,c); check(r1, 3, true, new Tuple<>(a1, 10), new Tuple<>(a2, 10), new Tuple<>(a3, 10)); check(r2, 3, true, new Tuple<>(a1, 10), new Tuple<>(a2, 10), new Tuple<>(a3, 10)); check(r3, 3, true, new Tuple<>(a1, 10), new Tuple<>(a2, 10), new Tuple<>(a3, 10)); } public void testMixedMulticastsToAll3Members() throws Exception { send(a, null, false, true, 10); send(b, null, false, true, 10); send(c, null, false, true, 10); sendStableMessages(a,b,c); check(r1, 3, true, new Tuple<>(a1, 10), new Tuple<>(a2, 10), new Tuple<>(a3, 10)); check(r2, 3, true, new Tuple<>(a1, 10), new Tuple<>(a2, 10), new Tuple<>(a3, 10)); check(r3, 3, true, new Tuple<>(a1, 10), new Tuple<>(a2, 10), new Tuple<>(a3, 10)); } private static void send(JChannel sender_channel, Address dest, boolean oob, int num_msgs) throws Exception { send(sender_channel, dest, oob, false, num_msgs); } private static void send(JChannel sender_channel, Address dest, boolean oob, boolean mixed, int num_msgs) throws Exception { long seqno=1; for(int i=0; i < num_msgs; i++) { Message msg=new Message(dest, seqno++); if(mixed) { if(i % 2 == 0) msg.setFlag(Message.Flag.OOB); } else if(oob) { msg.setFlag(Message.Flag.OOB); } sender_channel.send(msg); } } private static void sendStableMessages(JChannel ... channels) { for(JChannel ch: channels) { STABLE stable=ch.getProtocolStack().findProtocol(STABLE.class); if(stable != null) stable.gc(); } } protected static void removeDUPL(JChannel ... channels) { for(JChannel ch: channels) { DUPL dupl=ch.getProtocolStack().findProtocol(DUPL.class); if(dupl != null) { dupl.setCopyMulticastMsgs(false); dupl.setCopyUnicastMsgs(false); } } } private void createChannels(boolean copy_multicasts, boolean copy_unicasts, int num_outgoing_copies, int num_incoming_copies) throws Exception { a=createChannel(true, 3, "A"); DUPL dupl=new DUPL(copy_multicasts, copy_unicasts, num_incoming_copies, num_outgoing_copies); ProtocolStack stack=a.getProtocolStack(); stack.insertProtocol(dupl,ProtocolStack.Position.BELOW,NAKACK2.class); b=createChannel(a, "B"); c=createChannel(a, "C"); a.connect("DuplicateTest"); b.connect("DuplicateTest"); c.connect("DuplicateTest"); Util.waitUntilAllChannelsHaveSameView(20000, 1000, a, b, c); } @SafeVarargs private final void check(MyReceiver receiver, int expected_size, boolean oob, Tuple<Address,Integer>... vals) { Map<Address, Collection<Long>> msgs=receiver.getMsgs(); for(int i=0; i < 10; i++) { if(msgs.size() >= expected_size) break; Util.sleep(1000); } assert msgs.size() == expected_size : "expected size=" + expected_size + ", msgs: " + msgs.keySet(); for(Tuple<Address,Integer> tuple: vals) { Address addr=tuple.getVal1(); Collection<Long> list=msgs.get(addr); assert list != null : "no list available for " + addr; int expected_values=tuple.getVal2(); for(int i=0; i < 20; i++) { if(list.size() >= expected_values) break; Util.sleep(1000); sendStableMessages(a,b,c); } System.out.println("[" + receiver.getName() + "]: " + addr + ": " + list); assert list.size() == expected_values : addr + "'s list's size is not " + tuple.getVal2() + ", list: " + list + " (size=" + list.size() + ")"; if(!oob) // if OOB messages, ordering is not guaranteed check(addr, list); else checkPresence(list); } } private static void check(Address addr, Collection<Long> list) { long id=list.iterator().next(); for(long val: list) { assert val == id : "[" + addr + "]: val=" + val + " (expected " + id + "): list is " + list; id++; } } private static void checkPresence(Collection<Long> list) { for(long l=1; l <= 10; l++) { assert list.contains(l) : l + " is not in the list " + list; } } private static class MyReceiver extends ReceiverAdapter { final String name; private final ConcurrentMap<Address, Collection<Long>> msgs=new ConcurrentHashMap<>(); public MyReceiver(String name) { this.name=name; } public String getName() { return name; } public Map<Address, Collection<Long>> getMsgs() { return msgs; } public void receive(Message msg) { Address addr=msg.getSrc(); Long val=msg.getObject(); Collection<Long> list=msgs.get(addr); if(list == null) { list=new ConcurrentLinkedQueue<>(); Collection<Long> tmp=msgs.putIfAbsent(addr, list); if(tmp != null) list=tmp; } list.add(val); } public void clear() { msgs.clear(); } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("receiver " + name).append(":\n"); for(Map.Entry<Address,Collection<Long>> entry: msgs.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } return sb.toString(); } } }