package org.jgroups.tests;
import org.jgroups.Global;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.ReceiverAdapter;
import org.jgroups.protocols.SHUFFLE;
import org.jgroups.protocols.pbcast.NAKACK2;
import org.jgroups.stack.ProtocolStack;
import org.jgroups.util.Util;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Tests ordering with SEQUENCER: NUM_THREADS send messages concurrently over 3 channels, each thread sending NUM_MSGS
* messages. At the end of the test, we assert that NUM_THREADS * NUM_MSGS messages have been received and that every
* receiver delivered all messages in the same order.
* @author Bela Ban
*/
@Test(groups=Global.STACK_INDEPENDENT,sequential=true)
public class SequencerOrderTest {
private JChannel c1, c2, c3;
private MyReceiver r1, r2, r3;
static final String GROUP="SequencerOrderTest";
static final int NUM_MSGS=50; // messages per thread
static final int NUM_THREADS=10;
static final int EXPECTED_MSGS=NUM_MSGS * NUM_THREADS;
static final String props="sequencer.xml";
private Sender[] senders=new Sender[NUM_THREADS];
protected final AtomicInteger num=new AtomicInteger(0);
@BeforeMethod
void setUp() throws Exception {
c1=new JChannel(props);
c1.setName("A");
c1.connect(GROUP);
r1=new MyReceiver("A");
c1.setReceiver(r1);
c2=new JChannel(props);
c2.setName("B");
c2.connect(GROUP);
r2=new MyReceiver("B");
c2.setReceiver(r2);
c3=new JChannel(props);
c3.setName("C");
c3.connect(GROUP);
r3=new MyReceiver("C");
c3.setReceiver(r3);
Util.waitUntilAllChannelsHaveSameSize(10000, 1000, c1,c2,c3);
for(int i=0; i < senders.length; i++)
senders[i]=new Sender(NUM_MSGS, num, c1, c2, c3);
}
@AfterMethod
void tearDown() throws Exception {
removeSHUFFLE(c3,c2,c1);
Util.close(c3, c2, c1);
}
@Test @SuppressWarnings("unchecked")
public void testBroadcastSequence() throws Exception {
insertShuffle(c1, c2, c3);
// use concurrent senders to send messages to the group
System.out.println("Starting " + senders.length + " sender threads (each sends " + NUM_MSGS + " messages)");
for(Sender sender: senders)
sender.start();
for(Sender sender: senders)
sender.join(60000);
System.out.println("Ok, senders have completed");
for(int i=0; i < 10; i++) {
if(r1.size() == EXPECTED_MSGS && r2.size() == EXPECTED_MSGS && r3.size() == EXPECTED_MSGS)
break;
Util.sleep(1000);
}
final List<String> l1=r1.getMsgs();
final List<String> l2=r2.getMsgs();
final List<String> l3=r3.getMsgs();
System.out.println("-- verifying messages on A, B and C");
verifyNumberOfMessages(EXPECTED_MSGS, l1, l2, l3);
verifySameOrder(EXPECTED_MSGS, l1, l2, l3);
}
protected static void insertShuffle(JChannel... channels) throws Exception {
for(JChannel ch: channels) {
SHUFFLE shuffle=new SHUFFLE();
shuffle.setDown(false);
shuffle.setUp(true);
shuffle.setMaxSize(10);
shuffle.setMaxTime(1000);
ch.getProtocolStack().insertProtocol(shuffle, ProtocolStack.BELOW, NAKACK2.class);
shuffle.init(); // starts the timer
}
}
protected static void removeSHUFFLE(JChannel ... channels) {
for(JChannel ch: channels) {
SHUFFLE shuffle=(SHUFFLE)ch.getProtocolStack().removeProtocol(SHUFFLE.class);
if(shuffle != null)
shuffle.destroy();
}
}
private static void verifyNumberOfMessages(int num_msgs, List<String> ... lists) throws Exception {
long end_time=System.currentTimeMillis() + 10000;
while(System.currentTimeMillis() < end_time) {
boolean all_correct=true;
for(List<String> list: lists) {
if(list.size() != num_msgs) {
all_correct=false;
break;
}
}
if(all_correct)
break;
Util.sleep(1000);
}
for(int i=0; i < lists.length; i++)
System.out.println("list #" + (i+1) + ": " + lists[i]);
for(int i=0; i < lists.length; i++)
assert lists[i].size() == num_msgs : "list #" + (i+1) + " should have " + num_msgs +
" elements, but has " + lists[i].size() + " elements";
System.out.println("OK, all lists have the same size (" + num_msgs + ")\n");
}
private static void verifySameOrder(int expected_msgs, List<String> ... lists) throws Exception {
for(int index=0; index < expected_msgs; index++) {
String val=null;
for(List<String> list: lists) {
if(val == null)
val=list.get(index);
else {
String val2=list.get(index);
assert val.equals(val2) : "found different values at index " + index + ": " + val + " != " + val2;
}
}
}
System.out.println("OK, all lists have the same order");
}
private static class Sender extends Thread {
final int num_msgs;
final JChannel[] channels;
final AtomicInteger num;
public Sender(int num_msgs, AtomicInteger num, JChannel ... channels) {
this.num_msgs=num_msgs;
this.num=num;
this.channels=channels;
}
public void run() {
for(int i=1; i <= num_msgs; i++) {
try {
JChannel ch=(JChannel)Util.pickRandomElement(channels);
String channel_name=ch.getName();
int number=num.incrementAndGet();
ch.send(null, channel_name + number);
}
catch(Exception e) {
}
}
}
}
private static class MyReceiver extends ReceiverAdapter {
final String name;
final List<String> msgs=new LinkedList<String>();
private MyReceiver(String name) {
this.name=name;
}
public List<String> getMsgs() {
return msgs;
}
public int size() {return msgs.size();}
public void receive(Message msg) {
String val=(String)msg.getObject();
if(val != null) {
synchronized(msgs) {
msgs.add(val);
}
}
}
}
}