package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.blocks.cs.BaseServer; import org.jgroups.blocks.cs.NioServer; import org.jgroups.blocks.cs.ReceiverAdapter; import org.jgroups.blocks.cs.TcpServer; import org.jgroups.util.Bits; import org.jgroups.util.CondVar; import org.jgroups.util.Condition; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.Test; import java.io.DataInput; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; /** * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL,singleThreaded=true) public class ServerUnitTest { protected static final InetAddress bind_addr; static { try { bind_addr=Util.getLocalhost(); } catch(UnknownHostException e) { throw new RuntimeException(e); } } public void testSetup() throws Exception { for(boolean nio : new boolean[]{false, true}) { try(BaseServer a=create(nio, 0); BaseServer b=create(nio, 0)) { Assert.assertNotSame(a.localAddress(), b.localAddress()); } } } public void testSendEmptyData() throws Exception { for(boolean nio : new boolean[]{false, true}) { try(BaseServer a=create(nio, 0)) { byte[] data=new byte[0]; Address myself=a.localAddress(); a.receiver(new ReceiverAdapter() {}); send(data, a, myself); } } } public void testSendNullData() throws Exception { for(boolean nio : new boolean[]{false, true}) { try(BaseServer a=create(nio, 0)) { Address myself=a.localAddress(); a.send(myself, null, 0, 0); // the test passes if send() doesn't throw an exception } } } public void testSendToSelf() throws Exception { for(boolean nio : new boolean[]{false, true}) { try(BaseServer a=create(nio, 0)) { long NUM=1000, total_time; Address myself=a.localAddress(); MyReceiver r=new MyReceiver(a, NUM, false); byte[] data="hello world".getBytes(); a.receiver(r); for(int i=0; i < NUM; i++) send(data, a, myself); log("sent " + NUM + " msgs"); r.waitForCompletion(20000); total_time=r.stop_time - r.start_time; log("number expected=" + r.getNumExpected() + ", number received=" + r.getNumReceived() + ", total time=" + total_time + " (" + (double)total_time / r.getNumReceived() + " ms/msg)"); Assert.assertEquals(r.getNumExpected(), r.getNumReceived()); } } } public void testSendToAll() throws Exception { for(boolean nio : new boolean[]{false, true}) { try(BaseServer a=create(nio, 0); BaseServer b=create(nio, 0)) { long NUM=1000, total_time; MyReceiver r1=new MyReceiver(a, NUM, false); MyReceiver r2=new MyReceiver(b, NUM, false); byte[] data="hello world".getBytes(); // send uncast to establish connection to s2: // a.send(b.localAddress(), new byte[1000], 0, 1000); send(data, a, b.localAddress()); Util.sleep(1000); a.receiver(r1); b.receiver(r2); for(int i=0; i < NUM; i++) send(data, a, null); log("sent " + NUM + " msgs"); r2.waitForCompletion(20000); total_time=r2.stop_time - r2.start_time; log("number expected=" + r2.getNumExpected() + ", number received=" + r2.getNumReceived() + ", total time=" + total_time + " (" + (double)total_time / r2.getNumReceived() + " ms/msg)"); Assert.assertEquals(r2.getNumExpected(), r2.getNumReceived()); assert r1.getNumReceived() == 0 || r1.getNumReceived() > 0; } } } public void testSendToOther() throws Exception { for(boolean nio : new boolean[]{false, true}) { try(BaseServer a=create(nio, 0); BaseServer b=create(nio, 0)) { long NUM=1000, total_time; Address other=b.localAddress(); MyReceiver r=new MyReceiver(b, NUM, false); byte[] data="hello world".getBytes(); b.receiver(r); for(int i=0; i < NUM; i++) send(data, a, other); log("sent " + NUM + " msgs"); r.waitForCompletion(20000); total_time=r.stop_time - r.start_time; log("number expected=" + r.getNumExpected() + ", number received=" + r.getNumReceived() + ", total time=" + total_time + " (" + (double)total_time / r.getNumReceived() + " ms/msg)"); Assert.assertEquals(r.getNumExpected(), r.getNumReceived()); } } } public void testSendToOtherGetResponse() throws Exception { for(boolean nio : new boolean[]{false, true}) { try(BaseServer a=create(nio, 0); BaseServer b=create(nio, 0)) { long NUM=1000, total_time; Address other=b.localAddress(); MyReceiver r1=new MyReceiver(a, NUM, false); MyReceiver r2=new MyReceiver(b, NUM, true); // send response byte[] data="hello world".getBytes(); a.receiver(r1); b.receiver(r2); for(int i=0; i < NUM; i++) send(data, a, other); log("sent " + NUM + " msgs"); r1.waitForCompletion(20000); total_time=r1.stop_time - r1.start_time; log(String.format("r1.expected=%d, r1.received=%d, r2.expected=%d, r2.received=%d, r2.sent=%d, total time=%d (%.2f ms/msg)", r1.getNumExpected(), r1.getNumReceived(), r1.getNumExpected(), r2.getNumReceived(), r2.num_sent.get(), total_time, (double)total_time / r1.getNumReceived() )); Assert.assertEquals(r1.getNumReceived(), r1.getNumExpected()); } } } /** * A connects to B and B connects to A at the same time. This test makes sure we only have <em>one</em> connection, * not two, e.g. a spurious connection. Tests http://jira.jboss.com/jira/browse/JGRP-549.<p/> * Turned concurrent test into a simple sequential test. We're going to replace this code with NIO2 soon anyway... */ public void testReuseOfConnection() throws Exception { for(boolean nio : new boolean[]{false, true}) { try(BaseServer a=create(nio, 0); BaseServer b=create(nio, 0)) { int num_conns; num_conns=a.getNumConnections(); assert num_conns == 0; num_conns=b.getNumConnections(); assert num_conns == 0; Address addr1=a.localAddress(), addr2=b.localAddress(); byte[] data="hello world".getBytes(); send(data, a, addr2); send(data, b, addr1); String msg="A: " + a + "\nB: " + b; System.out.println(msg); waitForOpenConns(1, a, b); num_conns=a.getNumOpenConnections(); assert num_conns == 1 : "num_conns for A is " + num_conns + ", " + msg; num_conns=b.getNumOpenConnections(); assert num_conns == 1 : "num_conns for B is " + num_conns + ", " + msg; // done in a loop because connect() might be non-blocking (NioServer) connectionEstablished(a, addr2); connectionEstablished(b, addr1); } } } public void testConnectionCountOnStop() throws Exception { for(boolean nio : new boolean[]{false, true}) { try(BaseServer a=create(nio, 0); BaseServer b=create(nio, 0)) { Address addr1=a.localAddress(), addr2=b.localAddress(); byte[] data="hello world".getBytes(); send(data, a, addr1); // send to self assert a.getNumConnections() == 0; send(data, a, addr2); // send to other send(data, b, addr2); // send to self send(data, b, addr1); // send to other System.out.println("A:\n" + a + "\nB:\n" + b); int num_conns_table1=a.getNumConnections(), num_conns_table2=b.getNumConnections(); assert num_conns_table1 == 1 : "A should have 1 connection, but has " + num_conns_table1 + ": " + a; assert num_conns_table2 == 1 : "B should have 1 connection, but has " + num_conns_table2 + ": " + b; Util.close(b,a); waitForOpenConns(0, a, b); assert a.getNumOpenConnections() == 0 : "A should have 0 connections: " + a.printConnections(); assert b.getNumOpenConnections() == 0 : "B should have 0 connections: " + b.printConnections(); } } } public void testAsyncConnectThenSend() throws Exception { try(NioServer a=(NioServer)create(true, 0); NioServer b=(NioServer)create(true, 0)) { a.start(); b.start(); Address target=b.localAddress(); MyReceiver r=new MyReceiver(b, 2, false); b.receiver(r); // now send 2 msgs from A to B: this will connect async, buffer the 2 msgs, then send when connected byte[] buffer="hello world".getBytes(); send(buffer, a, target); send(buffer, a, target); r.waitForCompletion(20000); assert r.getNumReceived() == 2; } } protected static void send(byte[] request, BaseServer server, Address dest) { 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); } } static void log(String msg) { System.out.println("-- [" + Thread.currentThread() + "]: " + msg); } protected static BaseServer create(boolean nio, int port) { try { BaseServer retval=nio? new NioServer(bind_addr, port).maxSendBuffers(1024) : new TcpServer(bind_addr, port); retval.usePeerConnections(true); retval.start(); return retval; } catch(Exception ex) { return null; } } protected void waitForOpenConns(int expected, BaseServer... servers) { for(int i=0; i < 20; i++) { boolean all_ok=true; for(BaseServer server: servers) { if(server.getNumOpenConnections() != expected) { all_ok=false; break; } } if(all_ok) return; Util.sleep(1000); } } protected void connectionEstablished(BaseServer server, Address dest) { for(int i=0; i < 10; i++) { if(server.connectionEstablishedTo(dest)) break; Util.sleep(500); } assert server.connectionEstablishedTo(dest); } protected static class MyReceiver extends ReceiverAdapter { protected final long num_expected; protected final AtomicLong num_received=new AtomicLong(0), num_sent=new AtomicLong(0); protected long start_time=0, stop_time=0; protected final CondVar done=new CondVar(); protected boolean send_response=false; protected final long modulo; protected final BaseServer server; protected final Condition cond; MyReceiver(BaseServer server, long num_expected, boolean send_response) { this.server=server; this.num_expected=num_expected; this.send_response=send_response; start_time=System.currentTimeMillis(); modulo=num_expected / 10; cond=() -> num_received.get() >= num_expected; } public long getNumReceived() {return num_received.get();} public long getNumExpected() {return num_expected;} public void receive(Address sender, byte[] data, int offset, int length) { System.out.printf("[nio] from %s: %d bytes\n", sender, length); long tmp=num_received.incrementAndGet(); if(tmp >= num_expected) { synchronized(this) { if(stop_time == 0) stop_time=System.currentTimeMillis(); } done.signal(true); } if(send_response && tmp <= num_expected) { try { byte[] rsp=new byte[0]; send(rsp, server, sender); num_sent.incrementAndGet(); } catch(Exception e) { e.printStackTrace(); } } } public void receive(Address sender, DataInput in) throws Exception { int len=in.readInt(); byte[] buf=new byte[len]; in.readFully(buf); System.out.printf("[tcp] from %s: %d bytes\n", sender, len); long tmp=num_received.incrementAndGet(); if(tmp >= num_expected) { synchronized(this) { if(stop_time == 0) stop_time=System.currentTimeMillis(); } done.signal(true); } if(send_response && tmp <= num_expected) { try { byte[] rsp=new byte[0]; send(rsp, server, sender); num_sent.incrementAndGet(); } catch(Exception e) { e.printStackTrace(); } } } public void waitForCompletion(long timeout) throws Exception { done.waitFor(cond, timeout, TimeUnit.MILLISECONDS); } public String toString() { return String.format("expected=%d, received=%d\n", num_expected, num_received.get()); } } }