package org.jgroups.tests;
import org.jgroups.*;
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.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Tests overlapping merges, e.g. A: {A,B}, B: {A,B} and C: {A,B,C}. Tests unicast tables<br/>
* Related JIRA: https://jira.jboss.org/jira/browse/JGRP-940
* @author Bela Ban
*/
@Test(groups=Global.STACK_DEPENDENT,sequential=true)
public class OverlappingUnicastMergeTest extends ChannelTestBase {
private JChannel a, b, c;
private MyReceiver ra, rb, rc;
@BeforeMethod
void start() throws Exception {
ra=new MyReceiver("A"); rb=new MyReceiver("B"); rc=new MyReceiver("C");
a=createChannel(true, 3, "A");
a.setReceiver(ra);
b=createChannel(a, "B");
b.setReceiver(rb);
c=createChannel(a, "C");
c.setReceiver(rc);
modifyConfigs(a, b, c);
a.connect("OverlappingUnicastMergeTest");
b.connect("OverlappingUnicastMergeTest");
c.connect("OverlappingUnicastMergeTest");
View view=c.getView();
assertEquals("view is " + view, 3, view.size());
}
@AfterMethod
void tearDown() throws Exception {
Util.close(c,b,a);
}
public void testWithAllViewsInSync() throws Exception {
sendAndCheckMessages(5, a, b, c);
}
/**
* Verifies that unicasts are received correctly by all participants after an overlapping merge. The following steps
* are executed:
* <ol>
* <li/>Group is {A,B,C}, A is the coordinator
* <li/>MERGE2 is removed from all members
* <li/>VERIFY_SUSPECT is removed from all members
* <li/>Everyone sends 5 unicast messages to everyone else
* <li/>A VIEW(B,C) is injected into B and C
* <li/>B and C install {B,C}
* <li/>B and C trash the connection table for A in UNICAST
* <li/>A still has view {A,B,C} and all connection tables intact in UNICAST
* <li/>We now send N unicasts from everyone to everyone else, all the unicasts should be received.
* </ol>
*/
public void testWithViewBC() throws Exception {
System.out.println("A's view: " + a.getView());
// Inject view {B,C} into B and C:
View new_view=Util.createView(b.getAddress(), 10, b.getAddress(), c.getAddress());
injectView(new_view, b, c);
assertEquals("A's view is " + a.getView(), 3, a.getView().size());
assertEquals("B's view is " + b.getView(), 2, b.getView().size());
assertEquals("C's view is " + c.getView(), 2, c.getView().size());
sendAndCheckMessages(5, a, b, c);
}
public void testWithViewA() throws Exception {
// Inject view {A} into A, B and C:
View new_view=Util.createView(a.getAddress(), 10, a.getAddress());
injectView(new_view, a, b, c);
sendAndCheckMessages(5, a, b, c);
}
public void testWithViewC() throws Exception {
// Inject view {A} into A, B and C:
View new_view=Util.createView(c.getAddress(), 10, c.getAddress());
injectView(new_view, a, b, c);
sendAndCheckMessages(5, a, b, c);
}
public void testWithEveryoneHavingASingletonView() throws Exception {
// Inject view {A} into A, B and C:
injectView(Util.createView(a.getAddress(), 10, a.getAddress()), a);
injectView(Util.createView(b.getAddress(), 10, b.getAddress()), b);
injectView(Util.createView(c.getAddress(), 10, c.getAddress()), c);
sendAndCheckMessages(5, a, b, c);
}
private static void injectView(View view, JChannel ... channels) {
for(JChannel ch: channels) {
ch.down(new Event(Event.VIEW_CHANGE, view));
ch.up(new Event(Event.VIEW_CHANGE, view));
}
for(JChannel ch: channels) {
MyReceiver receiver=(MyReceiver)ch.getReceiver();
System.out.println("[" + receiver.name + "] view=" + ch.getView());
}
}
private void sendAndCheckMessages(int num_msgs, JChannel ... channels) throws Exception {
ra.clear(); rb.clear(); rc.clear();
// 1. send unicast messages
Set<Address> mbrs=new HashSet<Address>(channels.length);
for(JChannel ch: channels)
mbrs.add(ch.getAddress());
for(JChannel ch: channels) {
Address addr=ch.getAddress();
for(Address dest: mbrs) {
for(int i=1; i <= num_msgs; i++) {
ch.send(dest, "unicast msg #" + i + " from " + addr);
}
}
}
int total_msgs=num_msgs * channels.length;
MyReceiver[] receivers=new MyReceiver[channels.length];
for(int i=0; i < channels.length; i++)
receivers[i]=(MyReceiver)channels[i].getReceiver();
checkReceivedMessages(total_msgs, receivers);
}
private static void checkReceivedMessages(int num_ucasts, MyReceiver ... receivers) {
for(int i=0; i < 20; i++) {
boolean all_received=true;
for(MyReceiver receiver: receivers) {
List<Message> ucasts=receiver.getUnicasts();
int ucasts_received=ucasts.size();
if(num_ucasts != ucasts_received) {
all_received=false;
break;
}
}
if(all_received)
break;
Util.sleep(500);
}
for(MyReceiver receiver: receivers) {
List<Message> ucasts=receiver.getUnicasts();
int ucasts_received=ucasts.size();
System.out.println("receiver " + receiver + ": ucasts=" + ucasts_received);
assertEquals("ucasts for " + receiver + ": " + print(ucasts), num_ucasts, ucasts_received);
}
}
static String print(List<Message> list) {
StringBuilder sb=new StringBuilder();
for(Message msg: list) {
sb.append(msg.getSrc()).append(": ").append(msg.getObject()).append(" ");
}
return sb.toString();
}
private static void modifyConfigs(JChannel ... channels) throws Exception {
for(JChannel ch: channels) {
ProtocolStack stack=ch.getProtocolStack();
stack.removeProtocol("MERGE2");
stack.removeProtocol("VERIFY_SUSPECT");
stack.removeProtocol("FC");
}
}
private static class MyReceiver extends ReceiverAdapter {
final String name;
final List<Message> ucasts=new ArrayList<Message>(20);
public MyReceiver(String name) {
this.name=name;
}
public void receive(Message msg) {
Address dest=msg.getDest();
boolean mcast=dest == null;
if(!mcast)
ucasts.add(msg);
}
public void viewAccepted(View new_view) {
// System.out.println("[" + name + "] " + new_view);
}
public List<Message> getUnicasts() { return ucasts; }
public void clear() {ucasts.clear();}
public String toString() {
return name;
}
}
}