Package com.sun.multicast.reliable.applications.testtools

How to Use the JRMSTest harness.

See:
          Description

Interface Summary
CallProduct Defines all functions found in CallGDImpl.java
 

Class Summary
CallGDImpl Implements all RMI functions.
CallGDServer Starts the registry on port 1099, loads all functions contained in CallGDImpl.java
GDManager Manages the parsing of bytes and time for Graphing.
GraphManager This is a wrapper for PerfMon and RatePerfMon.
HostNameManager Used to create a Hashtable for storing hostnames found int the hostnames.txt file.
JRMSTest Main class that runs the JRMSTest harness.
LogFileManager Parses through all log and logbak files checking for succeeded and passed.
PacketReceiver uses rmi to call MCTestQA over the network.
PerfMon Basic class for creating bytes/time graph, although PerfMonCanvas does most of the work.
PerfMonCanvas PerfMonCanvas receives the GraphData objects from all the receivers, throws it into a GraphData vector and paints the Graph.
PerfObservable Used to Notify graph when a window has been placed on top, so a repaint can be called.
PropManager Manages properties file and stores properties.
RatePerfMon Basic class for creating rate/time graph, although RatePerfMonCanvas does most of the work.
RatePerfMonCanvas RateGraph Engine receives the GraphData objects from all the receivers, throws it into a GraphData vector and paints the Graph.
ResetGDManager Used to keep track of when the graph should be reset.
SelectVM Determines whether or not a machine is capable of running exact VM
StreamReceiver Kicks off the SimpleTesterQA.class using rsh
TestReceiver Parent Class for PacketReceiver and StreamReceiver.
 

Package com.sun.multicast.reliable.applications.testtools Description

How to Use the JRMSTest harness.

 

I. JRMSTest is located in the ../src/reliable/sample_code/testools directory.

 

 

Open a new shell window and type restart.pl to delete any old JDK processes you have and to start the RMI registry on each machine. Machines are ready when you see Waiting for invocation from clients... appear for each one. Next open another shell and type: JRMSTest.sh To see usage type: java JRMSTest -help

JRMSTest performs the following tasks:

 

  1. Waits for a serialized channel file to be written to the current directory.
  2. Uses the channel file to create the specified number of receivers
  3. Uses the channel file to create a sender.
  4. Uses the hostnames.txt file if it exists to create multiple receivers, one on each host.
  5. Uses the sender to transmit bytes using JRMS.
  6. Compares each byte that is sent to a running counter and flags any errors.
  7. If socket=packet, starts the Graph engine and uses RMI to send time and bytes received from each receiver to the Graph engine.
  8. Writes out a log file for each receiver and places it in the current directory.
  9. Writes out a log file for the sender and places it in the current directory.
  10. Waits for the sender to finish.
  11. Opens each log file and writes to system out if any failures where logged, and where found.
  12. A test passes when a byte that has been sent has been verified as being received.
  13. A logfile is successful when the process that starts the log has ended. If a logfile does not contain the word "succeeded", then either the process was killed before completion or it is still running.

 

II. You may set JRMSTest configuration parameters in the local default.properties file. Below is an example of the file with comments.

 

#default Properties

#Sun Jan 23 22:07:54 EST 2000

#chanelname can be any name

channelname=JRMSTest

#socket can be either packet or stream

socket=packet

#port can be any number

port=4321

#rverbose and must be true for graphing to work

rverbose=true

sverbose=true

#check with your net admin for a multicast ip address

# At bcn we can use 224.148.74.001 - 224.148.74.255

address=224.148.74.15

#maxrate=64000

maxrate=3000000

minrate=1000

ackwindow=32

#applname can be any name

applname=RMTest

CLASSPATH=/net/bcn.east/files3/home/henryw/jrms/version0.8/classes

#used in the event that a machine cannot run exact vm.

java1.2fcs=/net/labeast.east/files3/tools/jdk1.2fcs/bin/java

testtools=com.sun.multicast.reliable.applications.testtools

simple=com.sun.multicast.reliable.simple

#time to live

ttl=1

#take your pick

#intsent=3500000

#intsent=7000000

#intsent=35000

#intsent=14000000

intsent=560000000

#number of bytes (or ints) to send before ending test.

#intsent=1120000000

#For multiple hosts receivers should equal 1

receivers=1

hostfile=/home/henryw/jrms/version0.8/reliable/sample_code/testtools/hostnames.txt

#you should be logged into the server host before starting JRMSTest

server=apple

#server=mtest9

java.security.policy=client.policy

graph=true

#whether or not to skew the graph

skew=true

#javahome below can be used when you don't

#want to run with servers default java VM

#javahome=/home/henryw/jdk1.3/bin

 

III. Tech Note: You may also start JRSMTest.sh with these ten optional command line parameters:

 

  1. -r number of receivers - It is recommended that you start with a small number and work your way up in order to find out how many receivers your machine will be able to handle. If you leave out -r, one receiver will be created.
    1. Example: -r 2
  2. -a address - sets the multicast address. Multicast addresses are Class D and are between 224.0.0.0 and 239.255.255.255. The default address is 224.148.74.12
    1. Example: -a 224.148.73.11
  3. -c channelname - sets the serialized channel filename. The default filename is JRMSTest
    1. Example: -c TestChannel
  4. -p port number - defines the port number. The default is 4321
    1. Example: -p 1234
  5. -m maxrate - sets the maximum data transfer rate in bytes. The default is 64000
    1. Example: -m 32000
  6. -n minrate - sets the minimum data transfer rate in bytes. The default is 1000
    1. Example: -n 2000
  7. -t ttl - sets the time to live.
    1. -t 2
  8. -s applname - sets the application name
    1. Example: -s MyApp
  9. -v receive, send, or both - sets verbose logging in the log files. The default is to not use verbose logging.
    1. -v receive - uses verbose logging for receiver.log files only.
    2. -v send - uses verbose logging for the server.log file only.
    3. -v both - uses verbose logging for all log files.
  10. -i # of ints to send - sets the amount of data you would like to multicast.
    1. Example: -i 100000 - the server will send 100000 integers, 4 bytes each.

IV. How to create multiple receivers on different Hosts.

  1. Create a hostnames.txt file in the directory that you run JRMSTest.sh from.
  2. Put your list of hosts in the hostnames.txt file
  3. You may comment out a host with the pound (#) sign.
  4. Example:
  5. proteus

    bridge

    #masala

    apple

    bcn

     

    In the example above, the host masala will not be used to create a receiver.

     

V. Troubleshooting

  1. After starting restart.pl. There should be one JDK process running on each hostmachine in the hostnames.txt file
  2. Graphing only works with socket=packet in default.properties
  3. After using JRMSTest.sh and if the graph is not working, check the command line that is printed to the screen for each receiver and for the server. Put the command line into a shell script and run it that way. You should see error messages appear that will help you figure out why a process is not starting correctly.
  4. default.properties rverbose and sverbose should be set to true for graphing to work.
  5. default.properties CLASSPATH should be on a network drive where all host machines have access to it. Also check CLASSPATH - it's case sensitive
  6. default.properties server should be set to the server machine. This should be the same machine where restart.pl and JRMSTest.sh are run.
  7. Check to see that the graph engine default.properties graph is set to true
  8. Graph will only display 6 receivers.

This implementation is based on sample source code found in core JAVA Volume II-Advanced Features by Cay S. Horstmann and Gary Cornell Chapter 5 Remote Objects


I started by creating an interface with the following method prototypes.


package com.sun.multicast.reliable.applications.testtools;


import java.rmi.*;

import java.util.*;

import java.lang.*;


public interface CallProduct extends Remote {

void callMCTestQA(String commandline) throws RemoteException;

void startPerfMon() throws RemoteException;

void drawGraph(GraphData gd) throws RemoteException;

void printGraph() throws RemoteException;

boolean selectVM(String host) throws RemoteException;

void setReset(boolean set) throws RemoteException;

boolean getReset() throws RemoteException;

void testResetHash() throws RemoteException;

void setHost(String hostname) throws RemoteException;

}


Note that each method must throw RemoteException.


Next I wrote the body or implementation for these methods. We will go into the exact implementation of these methods at another time. Right now it's only important to see that there was one interface written above, and then the body of these methods appearing below in a complementing "Impl" class file:


package com.sun.multicast.reliable.applications.testtools;


import java.rmi.*;

import java.rmi.server.*;

import java.util.*;

import java.net.*;


public class CallGDImpl

extends UnicastRemoteObject

implements CallProduct {


private GraphData gd;

private int Counter = 0;

private GraphManager gm;

private SelectVM svm;

private HostNameManager hnm;

private ResetGDManager rgdm;

public CallGDImpl()

throws RemoteException {

}


public boolean selectVM(String host)

throws RemoteException {

svm = new SelectVM(host);

return svm.getExactVM();

}

public void startPerfMon()

throws RemoteException {


rgdm = ResetGDManager.getResetGDManager();

gm = GraphManager.getGraphManager();

gm.resetGData();

gm.resetRateGData();

gm.resetHostnames();

gm.resetRateHostnames();

gm.showGraph();

gm.showRateGraph();

}


public void drawGraph(GraphData gd)

throws RemoteException {


gm.drawGraph(gd);

gm.drawRateGraph(gd);

}


public void setReset(boolean set)

throws RemoteException {

rgdm.setReset(set);

}


public boolean getReset()

throws RemoteException {

return rgdm.getReset();

}


public void testResetHash()

throws RemoteException {

rgdm.testResetHash();

}

public void setHost(String hostname)

throws RemoteException {

rgdm.setHost(hostname);

}


public void printGraph()

throws RemoteException {

gm = GraphManager.getGraphManager();

gm.printGData();

}


public void callMCTestQA(String commandline)

throws RemoteException {


try {

Runtime runtime = Runtime.getRuntime();

System.out.println("runtime.exec(" + commandline + ")");

runtime.exec(commandline);

} catch(java.io.IOException e) {

System.out.println("Error: " + e);

}

}

}


Next I created a way for these methods to be distributed across the network. RMI uses naming and rebinding to accomplish this. This allows us to put these methods into the RMI registry across the network. Where does the rmi registry come from? It is supplied by JAVA.


package com.sun.multicast.reliable.applications.testtools;


import java.rmi.*;

import java.rmi.server.*;

import java.util.*;


public class CallGDServer {

public static void main(String args[]) {

try {

System.out.println("Constructing callback " +

"server implementations...");


CallGDImpl cgi1 =

new CallGDImpl();


System.out.println(

"Binding call server implementations to registry...");

Naming.rebind("CallMCTest", cgi1);


System.out.println(

"Waiting for invocation from clients...");

} catch (Exception e) {

System.out.println("Error: " + e);

}

}

}


That's all the written code that is necessary to make these methods available for the application. Now we have to compile, then compile again using rmic in order to create CallGDImpl_Stub.class.


I put that in the Makefile:CLASSPATH=$(CLASSPATH) rmic -v1.2 -d $(CLASSPATH) com.sun.multicast.reliable.applications.testtools.CallGDImpl


Finally you must start the RMI registry on each machine that you want these methods available.


rmiregistry&


And last you make java run the CallGDServer.class on each machine. Remember the CallGDServer.class was created to use naming.rebind to put these methods into the registry.


java com.sun.multicast.reliable.applications.testtools.CallGDServer&


Now that everything is up and running I only need to demonstrate how, these rmi object methods are called from the application.


First you create a variable of type CallProduct. Remember that CallProduct is an interface.


private CallProduct cp1;


Next you lookup the name that was bound to the registry:


url = "rmi://" + hostname + "/";

cp1 = (CallProduct)Naming.lookup(url +

"CallMCTest");


Notice that you must cast whatever object the Naming.lookup finds to the CallProduct interface.


Last you simply call the object method of your choice.


cp1.startPerfMon();


Finally, the compiler will tell you to put these rmi calls inside a try catch that includes...


} catch(java.rmi.RemoteException e) {

System.out.println("Error: " + e);

} catch(java.rmi.NotBoundException e) {

System.out.println("Error: " + e);

} catch(java.net.MalformedURLException e) {

System.out.println("Error: " + e);

}

}


JAVA2 forces RMI to use a security policy file. This file is defined in the java.security.policy key of any Properties file. I have named the security policy file

client.policy


You can restrict many things with the policy file for JAVA2. Info on this can be found in Chapter 9 of core JAVA 2 Volume II-Advanced Features


I decided to take the simplest route for now, and not restrict anything. This is what my

policy file looks like:


grant

{ permission java.security.AllPermission;

};


To include this in any running code, I use the (-D) option for java. -D will set a system property. Here is an example in the code:


java -D java.security.policy=client.policy ...

The Graph Nitty Gritty



OK let's get into the nitty gritty.


The Graphs are instantiated from the CallGDImpl.java file in the following method.


public void startPerfMon()

throws RemoteException {


rgdm = ResetGDManager.getResetGDManager();

gm = GraphManager.getGraphManager();

gm.resetGData();

gm.resetRateGData();

gm.resetHostnames();

gm.resetRateHostnames();

gm.showGraph();

gm.showRateGraph();

}


The first two important classes used here are the two singletons - Graph Manager.java and ResetGDManager.java, so lets take a look at those.


They are basically wrappers around the two files that actually are in charge of displaying the Performance and Rate Graphs, PerfMon.java and RatePerfMon.java I wanted to use inheritance on PerfMon.java and RatePerfMon.java since they have so much in common, but have not gotten around to that yet. The wrapper was needed because PerfMon.java already extends Frame and I needed it to also extend Observable, so the graph would redraw after another window came in front of it.


First lets look at GraphManager.java


package com.sun.multicast.reliable.applications.testtools;


import java.util.*;


public class GraphManager extends Observable {

private static GraphManager graphmanager;

private Observable notifier = new PerfObservable();

private PerfMon Graph;

private RatePerfMon RateGraph;


private GraphManager() {

Graph = new PerfMon(notifier,0,0);

RateGraph = new RatePerfMon(notifier,0,500);

}


public static GraphManager getGraphManager() {

if (graphmanager == null) {

graphmanager = new GraphManager();

}

return graphmanager;

}

public void drawGraph(GraphData gd) {

Graph.setData(gd);

Graph.customPaint();

}


public void drawRateGraph(GraphData gd) {

RateGraph.setData(gd);

RateGraph.customPaint();

}


public void printGData() {

Graph.printGData();

}


public void showGraph() {

Graph.show();

}


public void showRateGraph() {

RateGraph.show();

}


public void resetGData() {

Graph.resetGData();

}

public void resetRateGData() {

RateGraph.resetGData();

}


public void resetHostnames() {

Graph.resetHostnames();

}


public void resetRateHostnames() {

RateGraph.resetHostnames();

}

}


Then just for a reference lets look at PerfMon.java as well:


package com.sun.multicast.reliable.applications.testtools;


import java.awt.*;

import java.util.*;

import java.awt.event.*;


class PerfMonCanvas extends Canvas implements Observer {

private Observable notifier;

private int i=0;

private Vector GData = new Vector();

private Enumeration e;

private Enumeration htkeys;

private GraphData gd;

private HostNameManager hnm;

private Hashtable ht;

private Color colors[] = {Color.blue, Color.red, Color.green,

Color.magenta, Color.orange, Color.black};


PerfMonCanvas(Observable notifier) {

notifier.addObserver(this);

this.notifier = notifier;

hnm = HostNameManager.getHostNameManager();

ht = hnm.getHashHosts();

}


public void paint(Graphics g) {

htkeys = ht.keys();

GraphData paintgd;

e = GData.elements();

Dimension d = getSize();

int cx = d.width;

int cy = d.height;

String hostname = "";

int value = 0;


while (htkeys.hasMoreElements()) {

hostname = (String)htkeys.nextElement();

value = ((Integer)ht.get(hostname)).intValue();


if (!hostname.equals("reset")) {

g.setColor(colors[value]);

g.fillOval(10,10 + (15*value),6,6);

g.drawString(hostname,25,17 + (15*value));

}

}

g.setColor(Color.black);

g.drawRect(3,3,125,10 + (5 + (15*(ht.size()-1))));


while(e.hasMoreElements()) {

paintgd = (GraphData)e.nextElement();

drawlines(g, paintgd, cy);

}

}


public void printGData() {

e = GData.elements();

Dimension d = getSize();

int counter = 0;

GraphData printgd;


while(e.hasMoreElements()) {

printgd = (GraphData)e.nextElement();

System.out.println(counter++);

System.out.println("Bytes: " + printgd.getBytes());

System.out.println("Time: " + printgd.getTime());

System.out.println("Host: " + printgd.getHost());

}

}


public synchronized void customPaint() {

Dimension d = getSize();

int cx = d.width;

int cy = d.height;

Integer line;

Graphics g = this.getGraphics();

if (g != null) {

drawlines(g, gd, cy);

}

}


public void setGData(GraphData gd) {

this.gd = gd;

GData.addElement(gd);

}


public void resetGData() {

GData.removeAllElements();

repaint();

}


public void resetHostnames() {

hnm = HostNameManager.getHostNameManager();

ht = hnm.getHashHosts();

}


private void drawlines(Graphics g, GraphData gd, int cy) {

try {

Integer line;


if (gd != null) {

line = (Integer)ht.get(gd.getHost());

switch(line.intValue()) {

case 0:

g.setColor(Color.blue);

g.drawLine((gd.getTime()/150)+5,

(int)((float)cy-gd.getBytes()/10000),

(gd.getTime()/150)+7,

(int)((float)cy-gd.getBytes()/10000));

break;

case 1:

g.setColor(Color.red);

g.drawLine((gd.getTime()/150) + 25,

(int)((float)cy-gd.getBytes()/10000),

(gd.getTime()/150)+27,

(int)((float)cy-gd.getBytes()/10000));

break;

case 2:

g.setColor(Color.green);

g.drawLine((gd.getTime()/150) + 45,

(int)((float)cy-gd.getBytes()/10000),

(gd.getTime()/150) + 47,

(int)((float)cy-gd.getBytes()/10000));

break;

case 3:

g.setColor(Color.magenta);

g.drawLine((gd.getTime()/150) + 65,

(int)((float)cy-gd.getBytes()/10000),

(gd.getTime()/150) + 67,

(int)((float)cy-gd.getBytes()/10000));

break;

case 4:

g.setColor(Color.orange);

g.drawLine((gd.getTime()/150) + 85,

(int)((float)cy-gd.getBytes()/10000),

(gd.getTime()/150) + 87,

(int)((float)cy-gd.getBytes()/10000));

break;

case 5:

g.setColor(Color.black);

g.drawLine((gd.getTime()/150) + 105,

(int)((float)cy-gd.getBytes()/10000),

(gd.getTime()/150) + 107,

(int)((float)cy-gd.getBytes()/10000));

break;

case 99: //This is reserved for resetting the Vector

GData.removeAllElements();

repaint();

default:

break;

}

}


} catch (NoSuchElementException e) {

}


}


public void update(Observable o, Object arg) {

paint(this.getGraphics());

}

}


public class PerfMon extends Frame {

private Vector GData = new Vector();

private PerfMonCanvas pmc;


public PerfMon(Observable notifier, int x, int y) {

super("Bytes/Time Performance Monitor");

addWindowListener(new DWAdapter());

setBackground(Color.white);

setLayout( new BorderLayout());

setResizable(true);

pmc = new PerfMonCanvas(notifier);

// ScrollPane sp = new ScrollPane();

// sp.add("Center", pmc);

// add(sp);

add("Center", pmc);

// setSize(640, 480);

setBounds(x, y, 640, 480);

// setSize(1280, 960);

// setSize(2560,1920);

setVisible(true);

show();


}


public void setData(GraphData gd) {

pmc.setGData(gd);

}


public void customPaint() {

pmc.customPaint();

}

public void printGData() {

pmc.printGData();

}


public void resetGData() {

pmc.resetGData();

}


public void resetHostnames() {

pmc.resetHostnames();

}

class DWAdapter extends WindowAdapter {

public void windowClosing(WindowEvent event) {

//System.exit(0);

closeWindow();

}

}


public void closeWindow() {

this.dispose();

}

public static void main(String[] args) {

Observable notifier = new PerfObservable();

PerfMon f = new PerfMon(notifier,0,0);

}

}






JavaTM Reliable MulticastTM Service version 1.1
Copyright (c) 2001, Sun Microsystems Laboratories, All rights reserved.