package org.jgroups.tests;
import org.jgroups.*;
import org.jgroups.stack.Protocol;
import org.jgroups.jmx.JmxConfigurator;
import org.jgroups.protocols.UNICAST;
import org.jgroups.protocols.UNICAST2;
import org.jgroups.util.Util;
import org.jgroups.util.Streamable;
import javax.management.MBeanServer;
import java.io.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
/**
* Tests the UNICAST by sending unicast messages between a sender and a receiver
*
* @author Bela Ban
*/
public class UnicastTest extends ReceiverAdapter {
private JChannel channel;
private final MyReceiver receiver=new MyReceiver();
static final String groupname="UnicastTest-Group";
private long sleep_time=0;
private boolean exit_on_end=false, busy_sleep=false, oob=false;
private int num_threads=1;
private int num_msgs=100000, msg_size=1000;
public void init(String props, long sleep_time, boolean exit_on_end, boolean busy_sleep, String name) throws Exception {
this.sleep_time=sleep_time;
this.exit_on_end=exit_on_end;
this.busy_sleep=busy_sleep;
channel=new JChannel(props);
if(name != null)
channel.setName(name);
channel.connect(groupname);
channel.setReceiver(receiver);
try {
MBeanServer server=Util.getMBeanServer();
JmxConfigurator.registerChannel(channel, server, "jgroups", channel.getClusterName(), true);
}
catch(Throwable ex) {
System.err.println("registering the channel in JMX failed: " + ex);
}
}
public void eventLoop() throws Exception {
int c;
while(true) {
System.out.print("[1] Send msgs [2] Print view [3] Print conns [4] Trash conn [5] Trash all conns" +
"\n[6] Set sender threads (" + num_threads + ") [7] Set num msgs (" + num_msgs + ") " +
"[8] Set msg size (" + Util.printBytes(msg_size) + ")" +
"\n[o] Toggle OOB (" + oob + ")\n[q] Quit\n");
System.out.flush();
c=System.in.read();
switch(c) {
case -1:
break;
case '1':
sendMessages();
break;
case '2':
printView();
break;
case '3':
printConnections();
break;
case '4':
removeConnection();
break;
case '5':
removeAllConnections();
break;
case '6':
setSenderThreads();
break;
case '7':
setNumMessages();
break;
case '8':
setMessageSize();
break;
case 'o':
oob=!oob;
System.out.println("oob=" + oob);
break;
case 'q':
channel.close();
return;
default:
break;
}
}
}
private void printConnections() {
Protocol prot=channel.getProtocolStack().findProtocol(UNICAST.class, UNICAST2.class);
if(prot instanceof UNICAST)
System.out.println(((UNICAST)prot).printConnections());
else if(prot instanceof UNICAST2)
System.out.println(((UNICAST2)prot).printConnections());
}
private void removeConnection() {
Address member=getReceiver();
if(member != null) {
Protocol prot=channel.getProtocolStack().findProtocol(UNICAST.class, UNICAST2.class);
if(prot instanceof UNICAST)
((UNICAST)prot).removeConnection(member);
else if(prot instanceof UNICAST2)
((UNICAST2)prot).removeConnection(member);
}
}
private void removeAllConnections() {
Protocol prot=channel.getProtocolStack().findProtocol(UNICAST.class, UNICAST2.class);
if(prot instanceof UNICAST)
((UNICAST)prot).removeAllConnections();
else if(prot instanceof UNICAST2)
((UNICAST2)prot).removeAllConnections();
}
void sendMessages() throws Exception {
Address destination=getReceiver();
if(destination == null) {
System.err.println("UnicastTest.sendMessages(): receiver is null, cannot send messages");
return;
}
if(num_threads > 1 && num_msgs % num_threads != 0) {
System.err.println("num_msgs (" + num_msgs + " ) has to be divisible by num_threads (" + num_threads + ")");
return;
}
System.out.println("sending " + num_msgs + " messages (" + Util.printBytes(msg_size) +
") to " + destination + ": oob=" + oob + ", " + num_threads + " sender thread(s)");
byte[] buf=Util.objectToByteBuffer(new StartData(num_msgs));
Message msg=new Message(destination, null, buf);
channel.send(msg);
long print=num_msgs / 10;
int msgs_per_sender=num_msgs / num_threads;
Sender[] senders=new Sender[num_threads];
for(int i=0; i < senders.length; i++)
senders[i]=new Sender(msgs_per_sender, msg_size, destination, (int)print);
for(Sender sender: senders)
sender.start();
for(Sender sender: senders)
sender.join();
System.out.println("done sending " + num_msgs + " to " + destination);
}
void setSenderThreads() throws Exception {
int threads=Util.readIntFromStdin("Number of sender threads: ");
int old=this.num_threads;
this.num_threads=threads;
System.out.println("sender threads set to " + num_threads + " (from " + old + ")");
}
void setNumMessages() throws Exception {
num_msgs=Util.readIntFromStdin("Number of messages: ");
System.out.println("Set num_msgs=" + num_msgs);
}
void setMessageSize() throws Exception {
msg_size=Util.readIntFromStdin("Message size: ");
System.out.println("set msg_size=" + msg_size);
}
void printView() {
System.out.println("\n-- view: " + channel.getView() + '\n');
try {
System.in.skip(System.in.available());
}
catch(Exception e) {
}
}
private Address getReceiver() {
List<Address> mbrs=null;
int index;
BufferedReader reader;
String tmp;
try {
mbrs=channel.getView().getMembers();
System.out.println("pick receiver from the following members:");
int i=0;
for(Address mbr: mbrs) {
if(mbr.equals(channel.getAddress()))
System.out.println("[" + i + "]: " + mbr + " (self)");
else
System.out.println("[" + i + "]: " + mbr);
i++;
}
System.out.flush();
System.in.skip(System.in.available());
reader=new BufferedReader(new InputStreamReader(System.in));
tmp=reader.readLine().trim();
index=Integer.parseInt(tmp);
return mbrs.get(index); // index out of bounds caught below
}
catch(Exception e) {
System.err.println("UnicastTest.getReceiver(): " + e);
return null;
}
}
public static void main(String[] args) {
long sleep_time=0;
boolean exit_on_end=false;
boolean busy_sleep=false;
String props=null;
String name=null;
for(int i=0; i < args.length; i++) {
if("-props".equals(args[i])) {
props=args[++i];
continue;
}
if("-sleep".equals(args[i])) {
sleep_time=Long.parseLong(args[++i]);
continue;
}
if("-exit_on_end".equals(args[i])) {
exit_on_end=true;
continue;
}
if("-busy_sleep".equals(args[i])) {
busy_sleep=true;
continue;
}
if("-name".equals(args[i])) {
name=args[++i];
continue;
}
help();
return;
}
try {
UnicastTest test=new UnicastTest();
test.init(props, sleep_time, exit_on_end, busy_sleep, name);
test.eventLoop();
}
catch(Exception ex) {
System.err.println(ex);
}
}
static void help() {
System.out.println("UnicastTest [-help] [-props <props>] [-sleep <time in ms between msg sends] " +
"[-exit_on_end] [-busy-sleep] [-name name]");
}
public abstract static class Data implements Streamable {
}
public static class StartData extends Data {
long num_values=0;
public StartData() {}
StartData(long num_values) {
this.num_values=num_values;
}
public void writeTo(DataOutput out) throws Exception {
out.writeLong(num_values);
}
public void readFrom(DataInput in) throws Exception {
num_values=in.readLong();
}
}
public static class Value extends Data {
long value=0;
byte[] buf=null;
public Value() {
}
Value(long value, int len) {
this.value=value;
if(len > 0)
this.buf=new byte[len];
}
public void writeTo(DataOutput out) throws Exception {
out.writeLong(value);
if(buf != null) {
out.writeInt(buf.length);
out.write(buf, 0, buf.length);
}
else {
out.writeInt(0);
}
}
public void readFrom(DataInput in) throws Exception {
value=in.readLong();
int len=in.readInt();
if(len > 0) {
buf=new byte[len];
in.readFully(buf, 0, len);
}
}
}
private class Sender extends Thread {
private final int number_of_msgs;
private final int message_size;
private final Address destination;
private final int print;
public Sender(int num_msgs, int msg_size, Address destination, int print) {
this.number_of_msgs=num_msgs;
this.message_size=msg_size;
this.destination=destination;
this.print=print;
}
public void run() {
for(int i=1; i <= number_of_msgs; i++) {
Value val=new Value(i, message_size);
byte[] buf=new byte[0];
try {
buf=Util.objectToByteBuffer(val);
Message msg=new Message(destination, null, buf);
if(oob)
msg.setFlag(Message.OOB);
if(i > 0 && print > 0 && i % print == 0)
System.out.println("-- sent " + i);
channel.send(msg);
if(sleep_time > 0)
Util.sleep(sleep_time, busy_sleep);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
}
private class MyReceiver extends ReceiverAdapter {
private boolean started=false;
private long start=0, stop=0;
private long tmp=0, num_values=0;
private long total_time=0, msgs_per_sec, print;
private AtomicLong current_value=new AtomicLong(0), total_bytes=new AtomicLong(0);
public void receive(Message msg) {
Data data;
try {
data=(Data)Util.objectFromByteBuffer(msg.getRawBuffer(), msg.getOffset(), msg.getLength());
}
catch(Exception e) {
e.printStackTrace();
return;
}
if(data instanceof StartData) {
if(started) {
System.err.println("UnicastTest.run(): received START data, but am already processing data");
}
else {
started=true;
current_value.set(0); // first value to be received
tmp=0;
num_values=((StartData)data).num_values;
print=num_values / 10;
total_bytes.set(0);
start=System.currentTimeMillis();
}
}
else
if(data instanceof Value) {
tmp=((Value)data).value;
long new_val=current_value.incrementAndGet();
if(((Value)data).buf != null)
total_bytes.addAndGet(((Value)data).buf.length);
if(print > 0 && new_val % print == 0)
System.out.println("received " + current_value);
if(new_val >= num_values) {
stop=System.currentTimeMillis();
total_time=stop - start;
msgs_per_sec=(long)(num_values / (total_time / 1000.0));
double throughput=total_bytes.get() / (total_time / 1000.0);
System.out.println("-- received " + num_values + " messages (" + Util.printBytes(total_bytes.get()) +
") in " + total_time + " ms (" + msgs_per_sec + " messages/sec, " +
Util.printBytes(throughput) + " / sec)");
started=false;
if(exit_on_end)
System.exit(0);
}
}
}
public void viewAccepted(View new_view) {
System.out.println("** view: " + new_view);
}
}
}