package org.jgroups.protocols;
import org.jgroups.*;
import org.jgroups.protocols.pbcast.GMS;
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.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Tests the FORWARD_TO_COORD protocol
* @author Bela Ban
* @since 3.2
*/
@Test(groups=Global.FUNCTIONAL,sequential=true)
public class FORWARD_TO_COORD_Test {
protected static final int NUM=3; // number of members
protected static final int BASE='A';
protected final JChannel[] channels=new JChannel[NUM];
protected final MyReceiver[] receivers=new MyReceiver[NUM];
@BeforeMethod
void setUp() throws Exception {
System.out.print("Connecting channels: ");
for(int i=0; i < NUM; i++) {
channels[i]=Util.createChannel(new SHARED_LOOPBACK(),
new DISCARD(),
new PING().setValue("timeout",500).setValue("num_initial_members",NUM)
.setValue("force_sending_discovery_rsps", true),
new NAKACK2().setValue("use_mcast_xmit",false)
.setValue("discard_delivered_msgs",true)
.setValue("log_discard_msgs",true).setValue("log_not_found_msgs",true)
.setValue("xmit_table_num_rows",5)
.setValue("xmit_table_msgs_per_row",10),
new UNICAST2().setValue("xmit_table_num_rows",5).setValue("xmit_interval", 300)
.setValue("xmit_table_msgs_per_row",10)
.setValue("conn_expiry_timeout", 10000)
.setValue("stable_interval",30000)
.setValue("max_bytes",50000),
new GMS().setValue("print_local_addr",false)
.setValue("leave_timeout",2000)
.setValue("log_view_warnings",false)
.setValue("view_ack_collection_timeout",2000)
.setValue("log_collect_msgs",false),
new FORWARD_TO_COORD());
String name=String.valueOf((char)(i + BASE));
channels[i].setName(name);
receivers[i]=new MyReceiver();
channels[i].setReceiver(receivers[i]);
channels[i].connect("FORWARD_TO_COORD_Test");
System.out.print(name + " ");
if(i == 0)
Util.sleep(1500);
}
System.out.println("\n");
System.out.flush();
Util.waitUntilAllChannelsHaveSameSize(30000,1000,channels);
}
@AfterMethod
void tearDown() throws Exception {
for(int i=NUM-1; i >= 0; i--) {
ProtocolStack stack=channels[i].getProtocolStack();
String cluster_name=channels[i].getClusterName();
stack.stopStack(cluster_name);
stack.destroy();
}
}
/**
* Tests the default case: we have {A,B,C}, with A being the coordinator. C forwards a message to the current
* coordinator and therefore A must receive it.
*/
public void testSimpleForwarding() throws Exception {
Message msg=new Message(null, 22);
channels[NUM-1].down(new Event(Event.FORWARD_TO_COORD, msg)); // send on C, A must receive it
MyReceiver receiver=receivers[0];
for(int i=0; i < 20; i++) {
if(receiver.size() == 1)
break;
Util.sleep(500);
}
List<Integer> values=receiver.getValues();
System.out.println("A: received values: " + values);
assert values.size() == 1;
assert values.get(0) == 22;
for(int i=1; i < NUM; i++)
assert receivers[i].size() == 0;
}
/**
* Tests the case where C forwards a Message to A, but A leaves, so eventually B should receive C's message
*/
public void testForwardingWithCoordLeaving() throws Exception {
Message msg=new Message(null, 25);
DISCARD discard=(DISCARD)channels[NUM-1].getProtocolStack().findProtocol(DISCARD.class);
discard.setDropDownUnicasts(1);
// Sends the message to A, but C will discard it, so A will never get it
channels[NUM-1].down(new Event(Event.FORWARD_TO_COORD,msg));
// Now A leaves, C should resend the message to B
System.out.println("\n***** disconnecting A ******");
Util.close(channels[0]);
MyReceiver receiver=receivers[1]; // B
for(int i=0; i < 20; i++) {
if(receiver.size() == 1)
break;
Util.sleep(500);
}
System.out.println("Receivers");
printReceivers();
List<Integer> values=receiver.getValues();
System.out.println("B: received values: " + values);
assert values.size() == 1;
assert values.get(0) == 25;
}
/**
* Tests the case where C forwards a Message to A, but A leaves, so eventually B should receive C's message
*/
public void testForwardingWithCoordCrashing() throws Exception {
Message msg=new Message(null, 30);
DISCARD discard=(DISCARD)channels[0].getProtocolStack().findProtocol(DISCARD.class);
discard.setDiscardAll(true);
// Sends the message to A, but C will discard it, so A will never get it
channels[NUM-1].down(new Event(Event.FORWARD_TO_COORD,msg));
// Now A leaves, C should resend the message to B
System.out.println("***** crashing A ******");
Util.shutdown(channels[0]);
View view=Util.createView(channels[1].getAddress(), 5, channels[1].getAddress(), channels[2].getAddress());
System.out.println("Injecting view " + view + " into B and C");
for(JChannel ch: Arrays.asList(channels[1], channels[2])) {
GMS gms=(GMS)ch.getProtocolStack().findProtocol(GMS.class);
gms.up(new Event(Event.VIEW_CHANGE, view));
}
MyReceiver receiver=receivers[1]; // B
for(int i=0; i < 20; i++) {
if(receiver.size() == 1)
break;
Util.sleep(500);
}
System.out.println("Receivers");
printReceivers();
List<Integer> values=receiver.getValues();
System.out.println("B: received values: " + values);
assert values.size() == 1 : "values are " + values;
assert values.get(0) == 30;
}
/**
* Tests the case where a view is not installed at the same time in all members. C thinks B is the new coord and
* forwards a message to B. B, however doesn't yet have the same view, so it rejects (NOT_COORD message to C) the
* message. C in turn resends the message to B and so on. Only when B finally installs the view, will the message
* get accepted.
*/
public void testNotCoord() {
View new_view=Util.createView(channels[1].getAddress(), 3, channels[1].getAddress(),
channels[0].getAddress(), channels[2].getAddress());
System.out.println("Installing view " + new_view + " members A and C (not B !)");
for(JChannel ch: new JChannel[]{channels[0], channels[2]}) {
GMS gms=(GMS)ch.getProtocolStack().findProtocol(GMS.class);
gms.up(new Event(Event.VIEW_CHANGE, new_view));
}
for(JChannel ch: channels)
System.out.println(ch.getName() + ": view is " + ch.getView());
Message msg=new Message(null, 35);
// Sends the message to A, but C will discard it, so A will never get it
System.out.println("C: forwarding the message to B");
channels[NUM-1].down(new Event(Event.FORWARD_TO_COORD,msg));
Util.sleep(500);
System.out.println("Injecting view " + new_view + " into B and C");
GMS gms=(GMS)channels[1].getProtocolStack().findProtocol(GMS.class);
gms.up(new Event(Event.VIEW_CHANGE, new_view));
gms=(GMS)channels[NUM-1].getProtocolStack().findProtocol(GMS.class);
gms.up(new Event(Event.VIEW_CHANGE, new_view));
MyReceiver receiver=receivers[1]; // B
for(int i=0; i < 20; i++) {
if(receiver.size() == 1)
break;
Util.sleep(500);
}
System.out.println("Receivers");
printReceivers();
List<Integer> values=receiver.getValues();
System.out.println("B: received values: " + values);
assert values.size() == 1;
assert values.get(0) == 35;
}
void printReceivers() {
for(int i=0; i < NUM; i++) {
System.out.println(channels[i].getName() + ": " + receivers[i].getValues() + ", view: " + channels[i].getView());
}
}
protected static class MyReceiver extends ReceiverAdapter {
protected final List<Integer> values=new ArrayList<Integer>();
public int size() {return values.size();}
public List<Integer> getValues() {return values;}
public void receive(Message msg) {values.add((Integer)msg.getObject());}
}
}