package org.jgroups.tests; import org.jgroups.*; import org.jgroups.AnycastAddress; import org.jgroups.util.Util; import javax.management.*; import java.io.*; import java.lang.management.ManagementFactory; import java.util.*; /** * Runs the Total Order Anycast protocol and saves the messages delivered * * Note: this is used for debugging * Note2: this needs to be clean :) * * @author Pedro Ruivo * @since 3.1 */ public class TestToaOrder { private static final String PROPS = "toa.xml"; private static final String CLUSTER = "test-toa-cluster"; private static final String OUTPUT_FILE_SUFFIX = "-messages.txt"; private static final String JMX_DOMAIN = "org.jgroups"; private JChannel jChannel; private MyReceiver receiver; private int numberOfNodes; private int numberOfMessages; private final List<Address> members = new LinkedList<Address>(); private long start; private long stop; private long sentBytes = 0; private long sentMessages = 0; private String config; public static void main(String[] args) throws InterruptedException { System.out.println("=============="); System.out.println("Test TOA Order"); System.out.println("=============="); ArgumentsParser argumentsParser = new ArgumentsParser(args); if (argumentsParser.isHelp()) { helpAndExit(); } else if(argumentsParser.isTestOrder()) { /*String[] paths = argumentsParser.getFilesPath(); int numberOfFiles = paths.length; ProcessFile[] threads = new ProcessFile[numberOfFiles]; for (int i = 0; i < threads.length; ++i) { threads[i] = new ProcessFile(paths[i]); threads[i].start(); } Map<String, MessageInfo> allMessages = new HashMap<String, MessageInfo>(); for (ProcessFile processFile : threads) { processFile.join(); for (MessageInfo messageInfo : processFile.list) { String message = messageInfo.message; if (!allMessages.containsKey(message)) { allMessages.put(message, messageInfo); } else { allMessages.get(message).join(messageInfo); } } } for (MessageInfo messageInfo : allMessages.values()) { messageInfo.check(); } System.out.println("============= FINISHED ============="); System.exit(0);*/ } TestToaOrder test = new TestToaOrder( argumentsParser.getNumberOfNodes(), argumentsParser.getNumberOfMessages(), argumentsParser.getConfig()); try { test.startTest(); } catch (Exception e) { System.err.println("Error while executing the test: " + e.getMessage()); e.printStackTrace(); System.exit(1); } finally { test.closeJChannel(); System.out.println("============= FINISHED ============="); } System.exit(0); } private static void helpAndExit() { System.out.println("usage: " + TestToaOrder.class.getCanonicalName() + " <option>"); System.out.println("Options:"); System.out.println(" -h \tshow this message"); System.out.println(" -nr-nodes <value> \tnumber of nodes"); System.out.println(" -nr-messages <values> \tnumber of messages to send by each node"); System.out.println(" -config <file> \tthe JGroup's configuration file"); System.exit(1); } // ====================== arguments parser ====================== private static class ArgumentsParser { private static final int NR_NODES = 4; private static final int NR_MESSAGES = 1000; private String[] args; private int numberOfNodes = -1; private int numberOfMessages = -1; private boolean help = false; private boolean testOrder = false; private String[] filesPath = null; private String config = PROPS; public ArgumentsParser(String[] args) { this.args = args; parse(); checkConfig(); } private void parse() { try { for (int i = 0; i < args.length; ++i) { if ("-h".equals(args[i])) { help = true; } else if ("-nr-nodes".equals(args[i])) { numberOfNodes = Integer.parseInt(args[++i]); if (numberOfNodes < NR_NODES) { System.err.println("Number of nodes must be greater or equal to " + NR_NODES); System.exit(1); } } else if ("-nr-messages".equals(args[i])) { numberOfMessages = Integer.parseInt(args[++i]); if (numberOfMessages <= 0) { System.err.println("Number of messages must be greater than 0"); System.exit(1); } } else if ("-config".equals(args[i])) { config = args[++i]; } else { System.err.println("Unknown argument: " +args[i]); helpAndExit(); } } } catch (Throwable t) { System.err.println("Error processing arguments: " + t.getMessage()); t.printStackTrace(); System.exit(1); } } private void checkConfig() { if (numberOfNodes == -1) { numberOfNodes = NR_NODES; } if (numberOfMessages == -1) { numberOfMessages = NR_MESSAGES; } } public boolean isHelp() { return help; } public boolean isTestOrder() { return testOrder; } public int getNumberOfNodes() { return numberOfNodes; } public int getNumberOfMessages() { return numberOfMessages; } public String[] getFilesPath() { return filesPath; } public String getConfig() { return config; } } // ====================== receiver ====================== private static class MyReceiver extends ReceiverAdapter { private int expectedMembers; private int members = 0; private final List<String> messageList; private final TestToaOrder testGroupMulticastOrder; private long start = 0; private long stop = 0; private long receivedBytes = 0; private int receivedMsgs = 0; public MyReceiver(int expectedMembers, TestToaOrder testGroupMulticastOrder) { this.expectedMembers = expectedMembers; this.testGroupMulticastOrder = testGroupMulticastOrder; this.messageList = new LinkedList<String>(); } @Override public void receive(Message msg) { DataMessage dataMessage = (DataMessage) msg.getObject(); switch (dataMessage.type) { case DataMessage.FINISH: testGroupMulticastOrder.memberFinished(msg.getSrc()); break; case DataMessage.DATA: if (start == 0) { start = System.nanoTime(); } synchronized (messageList) { messageList.add(dataMessage.data); } receivedBytes += (dataMessage.data.getBytes().length + 1); receivedMsgs++; stop = System.nanoTime(); break; default: break; } } @Override public void viewAccepted(View view) { System.out.println("New View: " + view); super.viewAccepted(view); synchronized (this) { members = view.getMembers().size(); this.notify(); } } public synchronized void waitUntilClusterIsFormed() throws InterruptedException { while (members < expectedMembers) { System.out.println("Number of members is not the expected: " + members + " of " + expectedMembers); this.wait(); } } public void await(int expectedMessages) throws InterruptedException { int actualSize; while (true) { synchronized (messageList) { actualSize = messageList.size(); } if (actualSize < expectedMessages) { System.out.println("waiting messages... " + actualSize + " of " + expectedMessages); Thread.sleep(10000); } else { break; } } } public List<String> getMessageList() { return messageList; } public void printReceiverInfo() { System.out.println("+++ Receiver Information +++"); double duration = stop - start; duration /= 1000000.0; //nano to millis System.out.println("+ Duration (msec) = " + duration); System.out.println("+ Received Bytes = " + receivedBytes); System.out.println("+ Received Messages = " + receivedMsgs); duration /= 1000.0; //millis to sec System.out.println("---------------------"); System.out.println("+ Receiving Throughput (bytes/sec) = " + (receivedBytes / duration)); System.out.println("+ Receiving Messages (messages/sec) = " + (receivedMsgs / duration)); System.out.println("-------------------------------------"); } } // ====================== messages info (deliver before and after) ================ /*private static class MessageInfo { private String message; private Set<String> deliveredBefore = new HashSet<String>(); private Set<String> deliveredAfter = new HashSet<String>(); public void join(MessageInfo messageInfo) { this.deliveredAfter.addAll(messageInfo.deliveredAfter); this.deliveredBefore.addAll(messageInfo.deliveredBefore); } public void check() { Set<String> intersect = new HashSet<String>(deliveredBefore); intersect.retainAll(deliveredAfter); if (!intersect.isEmpty()) { System.err.println("ERROR: WRONG ORDER! messages " + intersect + " was delivered before and after this" + " message " + message); } } } private static class MessageInfo2 extends MessageInfo { private String message; private Set<MessageInfo2> deliveredBefore = new HashSet<MessageInfo2>(); @Override public void join(MessageInfo messageInfo) { deliveredBefore.addAll(((MessageInfo2)messageInfo).deliveredBefore); } @Override public void check() { for (MessageInfo2 messageInfo2 : deliveredBefore) { if (messageInfo2.deliveredBefore.contains(this)) { System.err.println("ERROR: WRONG ORDER! This message " + message + " was delivered before and after the" + " message " + messageInfo2.message); } } } }*/ //======================= thread processing each input file ===================== /*private static class ProcessFile extends Thread { private String filepath; public List<MessageInfo> list = new LinkedList<MessageInfo>(); private ProcessFile(String filepath) { super(); this.filepath = filepath; } @Override public void run() { runV2(); } public void runV1() { try { Set<String> previously = new HashSet<String>(); BufferedReader bufferedReader = new BufferedReader(new FileReader(filepath)); String message; while ((message = bufferedReader.readLine()) != null) { MessageInfo messageInfo = new MessageInfo(); messageInfo.message = message; messageInfo.deliveredBefore.addAll(previously); for (MessageInfo aux : list) { aux.deliveredAfter.add(message); } list.add(messageInfo); previously.add(message); } } catch (FileNotFoundException e) { e.printStackTrace(); // TODO: Customise this generated block } catch (IOException e) { e.printStackTrace(); // TODO: Customise this generated block } } public void runV2() { try { Set<MessageInfo2> previously = new HashSet<MessageInfo2>(); BufferedReader bufferedReader = new BufferedReader(new FileReader(filepath)); String message; while ((message = bufferedReader.readLine()) != null) { MessageInfo2 messageInfo = new MessageInfo2(); messageInfo.message = message; messageInfo.deliveredBefore.addAll(previously); list.add(messageInfo); previously.add(messageInfo); } } catch (FileNotFoundException e) { e.printStackTrace(); // TODO: Customise this generated block } catch (IOException e) { e.printStackTrace(); // TODO: Customise this generated block } } }*/ //======================= data message ======================= private static class DataMessage implements Serializable { public transient static final byte FINISH = 1; //1 << 0 public transient static final byte DATA = 1 << 1; private static final long serialVersionUID=5946678490588947910L; private byte type; private String data; } // ====================== other methods ====================== public TestToaOrder(int numberOfNodes, int numberOfMessages, String config) { this.numberOfNodes = numberOfNodes; this.numberOfMessages = numberOfMessages; this.config = config; } private void createJChannel() throws Exception { System.out.println("Creating Channel"); receiver = new MyReceiver(numberOfNodes, this); jChannel = new JChannel(config); jChannel.setReceiver(receiver); jChannel.connect(CLUSTER); receiver.waitUntilClusterIsFormed(); Util.registerChannel(jChannel, JMX_DOMAIN); members.addAll(jChannel.getView().getMembers()); } private AnycastAddress getDestinations(List<Address> members) { int rand = members.indexOf(jChannel.getAddress()); AnycastAddress address = new AnycastAddress(); address.add(members.get(rand++ % members.size()), members.get(rand++ % members.size()), members.get(rand % members.size())); return address; } private void sendMessages() throws Exception { System.out.println("Start sending messages..."); String address = jChannel.getAddressAsString(); List<Address> mbrs = jChannel.getView().getMembers(); start = System.nanoTime(); for (int i = 0; i < numberOfMessages; ++i) { AnycastAddress dst = getDestinations(mbrs); Message message = new Message(); message.setDest(dst); DataMessage dataMessage = new DataMessage(); dataMessage.type = DataMessage.DATA; dataMessage.data = address + ":" + i; message.setObject(dataMessage); jChannel.send(message); sentBytes += (dataMessage.data.getBytes().length + 1); sentMessages++; } stop = System.nanoTime(); System.out.println("Finish sending messages..."); } private void awaitUntilAllMessagesAreReceived() throws InterruptedException { int expectedMessages = 3 * numberOfMessages; receiver.await(expectedMessages); } private void awaitUntilAllFinishes() throws Exception { DataMessage dataMessage = new DataMessage(); dataMessage.type = DataMessage.FINISH; dataMessage.data = null; jChannel.send(null, dataMessage); synchronized (members) { if (!members.isEmpty()) { members.wait(); } } } public void printSenderInfo() { System.out.println("+++ Sender Information +++"); double duration = stop - start; duration /= 1000000.0; //nano to millis System.out.println("+ Duration (msec) = " + duration); System.out.println("+ Sent Bytes = " + sentBytes); System.out.println("+ Sent Messages = " + sentMessages); duration /= 1000.0; //millis to sec System.out.println("-------------------"); System.out.println("+ Sent Throughput (bytes/sec) = " + (sentBytes / duration)); System.out.println("+ Sent Messages (messages/sec) = " + (sentMessages / duration)); System.out.println("--------------------------------"); } public void memberFinished(Address addr) { synchronized (members) { members.remove(addr); if (members.isEmpty()) { members.notify(); } } } public void closeJChannel() { System.out.println("Close channel"); jChannel.close(); } public void startTest() throws Exception { System.out.println("Start testing..."); createJChannel(); sendMessages(); awaitUntilAllMessagesAreReceived(); String filePath = jChannel.getAddressAsString() + OUTPUT_FILE_SUFFIX; System.out.println("Writing messages in " + filePath); FileWriter fileWriter = new FileWriter(filePath); for (String s : receiver.getMessageList()) { fileWriter.write(s); fileWriter.write("\n"); } fileWriter.flush(); fileWriter.close(); System.out.println("All done!"); awaitUntilAllFinishes(); printSenderInfo(); receiver.printReceiverInfo(); printJMXStats(); } private static void printJMXStats() { MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); ObjectName groupMulticast = getGroupMulticastObjectName(mBeanServer); if (groupMulticast == null) { System.err.println("Unable to find the GROUP_MULTICAST protocol"); return ; } try { System.out.println("======== JMX STATS ========="); for (MBeanAttributeInfo mBeanAttributeInfo : mBeanServer.getMBeanInfo(groupMulticast).getAttributes()) { String attribute = mBeanAttributeInfo.getName(); String type = mBeanAttributeInfo.getType(); if (!type.equals("double") && !type.equals("int")) { continue; } System.out.println(attribute + "=" + mBeanServer.getAttribute(groupMulticast, attribute)); } System.out.println("======== JMX STATS ========="); } catch (Exception e) { System.err.println("Error collecting stats" + e.getLocalizedMessage()); } } private static ObjectName getGroupMulticastObjectName(MBeanServer mBeanServer) { for(ObjectName name : mBeanServer.queryNames(null, null)) { if(name.getDomain().equals(JMX_DOMAIN)) { if ("TOA".equals(name.getKeyProperty("protocol"))) { return name; } } } return null; } }