package org.jgroups.tests.byteman; import org.jboss.byteman.contrib.bmunit.BMNGRunner; import org.jboss.byteman.contrib.bmunit.BMScript; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.blocks.cs.BaseServer; import org.jgroups.blocks.cs.TcpServer; import org.jgroups.blocks.cs.NioServer; import org.jgroups.blocks.cs.ReceiverAdapter; import org.jgroups.util.Bits; import org.jgroups.util.ResourceManager; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.DataInput; import java.net.InetAddress; import java.util.ArrayList; import java.util.List; /** * Tests concurrent connection establishments in TcpServer * @author Bela Ban * @since 3.3 */ @Test(groups=Global.BYTEMAN,singleThreaded=true,dataProvider="configProvider") public class ServerTest extends BMNGRunner { protected BaseServer a, b; protected static final InetAddress loopback; protected MyReceiver receiver_a, receiver_b; protected static final int PORT_A, PORT_B; public static Address A=null, B=null; // need to be static for the byteman rule scripts to access them protected static final String STRING_A="a.req", STRING_B="b.req"; static { try { loopback=Util.getLocalhost(); PORT_A=ResourceManager.getNextTcpPort(loopback); PORT_B=ResourceManager.getNextTcpPort(loopback); } catch(Exception ex) { throw new RuntimeException(ex); } } @DataProvider protected Object[][] configProvider() { return new Object[][] { {create(false, PORT_A), create(false, PORT_B)}, {create(true, PORT_A), create(true, PORT_B)} }; } protected void setup(BaseServer one, BaseServer two) throws Exception { a=one; a.usePeerConnections(true); b=two; b.usePeerConnections(true); A=a.localAddress(); B=b.localAddress(); assert A.compareTo(B) < 0; a.receiver(receiver_a=new MyReceiver("A")); a.start(); b.receiver(receiver_b=new MyReceiver("B")); b.start(); } @AfterMethod protected void destroy() { Util.close(a, b); } public void testStart(BaseServer a, BaseServer b) throws Exception { setup(a, b); assert !a.hasConnection(B) && !b.hasConnection(A); assert a.getNumConnections() == 0 && b.getNumConnections() == 0; } public void testSimpleSend(BaseServer a, BaseServer b) throws Exception { setup(a,b); send(STRING_A, a, B); } /** * Tests A connecting to B, and then B connecting to A; no concurrent connections */ public void testSimpleConnection(BaseServer first, BaseServer second) throws Exception { setup(first,second); send("hello", a, B); waitForOpenConns(1, a, b); assert a.getNumOpenConnections() == 1 : "number of connections for conn_a: " + a.getNumOpenConnections(); assert b.getNumOpenConnections() == 1 : "number of connections for conn_b: " + b.getNumOpenConnections(); check(receiver_b.getList(),"hello"); send("hello", b, A); waitForOpenConns(1, a, b); assert a.getNumOpenConnections() == 1 : "number of connections for conn_a: " + a.getNumOpenConnections(); assert b.getNumOpenConnections() == 1 : "number of connections for conn_b: " + b.getNumOpenConnections(); check(receiver_b.getList(), "hello"); check(receiver_a.getList(), "hello"); } /** * Tests the case where A and B connect to each other concurrently: * <ul> * <li>A.accept(B) || B.accept(A)</li> * <li>A.connect(B) || B.connect(A)</li> * </ul> */ @BMScript(dir="scripts/ServerTest", value="testConcurrentConnect") // @Test(dataProvider="configProvider",invocationCount=5) public void testConcurrentConnect(BaseServer first, BaseServer second) throws Exception { setup(first, second); _testConcurrentConnect(1, 1, 0); } protected void _testConcurrentConnect(int expected_msgs_in_A, int expected_msgs_in_B, int alt_b) throws Exception { new Thread(new Sender(a,B, STRING_A), "sender-1").start(); new Thread(new Sender(b,A, STRING_B), "sender-2").start(); waitForOpenConns(1, a, b); assert a.getNumOpenConnections() == 1 : "expected 1 connection but got " + a.getNumOpenConnections() + ": " + a.printConnections(); assert b.getNumOpenConnections() == 1 : "expected 1 connection but got " + b.getNumOpenConnections() + ": " + b.printConnections(); List<String> list_a=receiver_a.getList(); List<String> list_b=receiver_b.getList(); for(int i=0; i < 10; i++) { if(list_a.size() == expected_msgs_in_A && (list_b.size() == expected_msgs_in_B || list_b.size() == alt_b)) break; Util.sleep(500); } System.out.println("list A=" + list_a + " (expected=" + expected_msgs_in_A + ")" + "\nlist B=" + list_b + "( expected=" + expected_msgs_in_B + " or " + alt_b + ")"); assert list_a.size() == expected_msgs_in_A && (list_b.size() == expected_msgs_in_B || list_b.size() == alt_b) : "list A=" + list_a + "\nlist B=" + list_b; } protected void check(List<String> list, String expected_str) { for(int i=0; i < 20; i++) { if(list.isEmpty()) Util.sleep(500); else break; } assert !list.isEmpty() && list.get(0).equals(expected_str) : " list: " + list + ", expected " + expected_str; } protected void waitForOpenConns(int expected, BaseServer... servers) { for(int i=0; i < 10; i++) { boolean all_ok=true; for(BaseServer server: servers) { if(server.getNumOpenConnections() != expected) { all_ok=false; break; } } if(all_ok) return; Util.sleep(500); } } protected BaseServer create(boolean nio, int port) { try { return nio? new NioServer(loopback, port) : new TcpServer(loopback, port); } catch(Exception ex) { return null; } } protected static void send(String str, BaseServer server, Address dest) { byte[] request=str.getBytes(); byte[] data=new byte[request.length + Global.INT_SIZE]; Bits.writeInt(request.length, data, 0); System.arraycopy(request, 0, data, Global.INT_SIZE, request.length); try { server.send(dest, data, 0, data.length); } catch(Exception e) { System.err.println("Failed sending a request to " + dest + ": " + e); } } protected static class Sender implements Runnable { protected final BaseServer server; protected final Address dest; protected final String req_to_send; public Sender(BaseServer server, Address dest, String req_to_send) { this.server=server; this.dest=dest; this.req_to_send=req_to_send; } public void run() { send(req_to_send, server, dest); } } protected static class MyReceiver extends ReceiverAdapter { protected final String name; protected final List<String> reqs=new ArrayList<>(); public MyReceiver(String name) { this.name=name; } public List<String> getList() {return reqs;} public void clear() {reqs.clear();} public void receive(Address sender, byte[] data, int offset, int length) { int len=Bits.readInt(data, offset); String str=new String(data, offset+Global.INT_SIZE, len); System.out.println("[" + name + "] received request \"" + str + "\" from " + sender); reqs.add(str); } public void receive(Address sender, DataInput in) throws Exception { int len=in.readInt(); byte[] data=new byte[len]; in.readFully(data, 0, data.length); String str=new String(data, 0, data.length); System.out.println("[" + name + "] received request \"" + str + "\" from " + sender); reqs.add(str); } } }