/*
VisAD system for interactive analysis and visualization of numerical
data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.Random;
import visad.BaseColorControl;
import visad.ColorAlphaControl;
import visad.Control;
import visad.ConstantMap;
import visad.Display;
import visad.DisplayImpl;
import visad.LocalDisplay;
import visad.RealType;
import visad.RemoteDisplayImpl;
import visad.ScalarMap;
import visad.VisADException;
import visad.collab.DisplaySyncImpl;
import visad.java2d.DisplayImplJ2D;
import visad.util.Delay;
/**
* Start up a bunch of collaborative clients, then have the
* server and clients go through a number of rounds where
* they all tweak their controls to trigger a bunch of events
* and, after a predetermined amount of time, check to make
* sure everyone is synchronized.<br>
* <br>
* <table>
* <tr><th>Option</th><th>Description</th></tr>
* <tr><td>-c <i>num</i></td><td>Number of clients</td></tr>
* <tr><td>-d <i>num</i></td><td>Seconds to delay * 100</td></tr>
* <tr><td>-e <i>num</i></td><td>Number of events per round</td></tr>
* <tr><td>-r <i>num</i></td><td>Number of rounds</td></tr>
* </table>
*/
public class TestEvents
{
private static Object waiter = new Object();
private static java.util.Random rand = null;
private static RealType mapType = null;
private int numClients = 6;
private int numRounds = 6;
private int numEvents = 6;
private long randSeed = -1;
private int delayUsecs = 150;
private boolean verbose = false;
public TestEvents(String[] args)
{
if (!processArgs(args)) {
System.err.println("Exiting...");
System.exit(1);
}
if (rand == null) {
if (randSeed == -1) {
rand = new Random();
} else {
rand = new Random(randSeed);
}
}
mapType = RealType.getRealType("Map");
DisplayImpl server = createServer();
if (server == null) {
System.err.println("Couldn't create server!");
System.exit(1);
return;
}
Bundle[] bundle = createBundle(server, numClients);
if (bundle == null) {
System.exit(1);
return;
}
boolean result = true;
for (int i = 0; i < numRounds; i++) {
if (verbose) {
System.err.println("--- Round "+i);
System.err.flush();
}
for (int j = 0; j < numEvents; j++) {
//new Delay(1000);
synchronized (waiter) { waiter.notifyAll(); }
Thread.yield();
}
for (int d = 0; d < 10; d++) {
new Delay(delayUsecs);
if (verbose) {
System.err.println("++ Finished Delay#" + d + ":" +
System.currentTimeMillis());
System.err.flush();
}
}
result &= compareAll(bundle);
}
StringBuffer dangle = null;
for (int i = 0; i < bundle.length; i++) {
DisplayImpl dpy = (DisplayImpl )bundle[i].getDisplay();
DisplaySyncImpl dsi = (DisplaySyncImpl )dpy.getDisplaySync();
if (dsi.isThreadRunning()) {
if (dangle == null) {
dangle = new StringBuffer(dsi.getName());
} else {
dangle.append(", ");
dangle.append(dsi.getName());
}
result = false;
}
}
if (dangle != null) {
dangle.insert(0, "Yikes ... thread running for ");
String dangleStr = dangle.toString();
System.out.println(dangleStr);
System.err.println(dangleStr);
}
System.out.flush();
System.err.flush();
if (!result) {
System.exit(1);
return;
}
System.out.println("Happy happy, joy joy!");
System.exit(0);
}
private boolean compareAll(Bundle[] bundle)
{
StringBuffer badList = null;
final TriggerControl tc = bundle[0].getTriggerControl();
final BaseColorControl cc = bundle[0].getColorControl();
boolean allTcCmp = true;
boolean allCcCmp = true;
for (int i = 1; i < bundle.length; i++) {
final TriggerControl btc = bundle[i].getTriggerControl();
final BaseColorControl bcc = bundle[i].getColorControl();
final boolean tcCmp = btc.equals(tc);
final boolean ccCmp = bcc.equals(cc);
if (!tcCmp || !ccCmp) {
DisplayImpl dpy = (DisplayImpl )bundle[i].getDisplay();
if (badList == null) {
badList = new StringBuffer();
} else {
badList.append(", ");
}
badList.append(dpy.getName());
badList.append('=');
if (!tcCmp) {
badList.append(btc);
allTcCmp = false;
if (!ccCmp) {
badList.append(',');
badList.append(bcc);
allCcCmp = false;
}
} else if (!ccCmp) {
badList.append(bcc);
allCcCmp = false;
}
}
}
final boolean matched = (badList == null);
if (!matched) {
badList.insert(0, ", got ");
if (!allCcCmp) {
badList.insert(0, cc);
}
if (!allTcCmp) {
if (!allCcCmp) {
badList.insert(0, ',');
}
badList.insert(0, tc);
}
badList.insert(0, "Wanted ");
String badStr = badList.toString();
System.err.println(badStr);
System.out.println(badStr);
}
return matched;
}
private DisplayImpl createServer()
{
DisplayImplJ2D dpy;
try {
dpy = new DisplayImplJ2D("root");
} catch (RemoteException re) {
return null;
} catch (VisADException ve) {
return null;
}
return dpy;
}
private Bundle[] createBundle(DisplayImpl server, int num)
{
Bundle[] bundle = new Bundle[num+1];
try {
bundle[0] = new Bundle(server, false);
} catch (RemoteException re) {
System.err.println("Couldn't create server wrapper");
re.printStackTrace();
return null;
} catch (VisADException ve) {
System.err.println("Couldn't create server wrapper");
ve.printStackTrace();
return null;
}
for (int i = 0; i < num; i++) {
try {
bundle[i+1] = new Bundle(server);
} catch (RemoteException re) {
System.err.println("Couldn't create client #" + i + "!");
re.printStackTrace();
return null;
} catch (VisADException ve) {
System.err.println("Couldn't create client #" + i + "!");
ve.printStackTrace();
return null;
}
}
return bundle;
}
private static final TriggerControl getTriggerCtl(DisplayImpl dpy)
{
Class tClass;
try {
tClass = Class.forName(TriggerControl.class.getName());
} catch (ClassNotFoundException cnfe) {
tClass = null;
}
return (TriggerControl )dpy.getControl(tClass, 0);
}
public boolean processArgs(String[] args)
{
boolean usage = false;
String className = getClass().getName();
int pt = className.lastIndexOf('.');
final int ds = className.lastIndexOf('$');
if (ds > pt) {
pt = ds;
}
String progName = className.substring(pt == -1 ? 0 : pt + 1);
for (int i = 0; args != null && i < args.length; i++) {
if (args[i].length() > 0 && args[i].charAt(0) == '-') {
char ch = args[i].charAt(1);
String str, result;
switch (ch) {
case 'c':
str = (args[i].length() > 2 ? args[i].substring(2) :
((i + 1) < args.length ? args[++i] : null));
if (str == null) {
System.err.println(progName +
": Missing number of clients for \"-c\"");
usage = true;
} else {
try {
numClients = Integer.parseInt(str);
} catch (NumberFormatException nfe) {
System.err.println(progName +
": Bad number of clients \"" + str + "\"");
numClients = 2;
usage = true;
}
if (numClients < 1) {
System.err.println(progName +
": Need at least one client!");
usage = true;
}
}
break;
case 'd':
str = (args[i].length() > 2 ? args[i].substring(2) :
((i + 1) < args.length ? args[++i] : null));
if (str == null) {
System.err.println(progName +
": Missing delay usecs for \"-d\"");
usage = true;
} else {
try {
delayUsecs = Integer.parseInt(str);
} catch (NumberFormatException nfe) {
System.err.println(progName +
": Bad delay usecs \"" + str + "\"");
delayUsecs = 100;
usage = true;
}
if (delayUsecs < 1) {
System.err.println(progName +
": Delay usecs needs to be" +
" greater than zero!");
usage = true;
}
}
break;
case 'e':
str = (args[i].length() > 2 ? args[i].substring(2) :
((i + 1) < args.length ? args[++i] : null));
if (str == null) {
System.err.println(progName +
": Missing number of events for \"-e\"");
usage = true;
} else {
try {
numEvents = Integer.parseInt(str);
} catch (NumberFormatException nfe) {
System.err.println(progName +
": Bad number of events \"" + str + "\"");
numEvents = 3;
usage = true;
}
if (numEvents < 1) {
System.err.println(progName +
": Need at least one event!");
usage = true;
}
}
break;
case 'r':
str = (args[i].length() > 2 ? args[i].substring(2) :
((i + 1) < args.length ? args[++i] : null));
if (str == null) {
System.err.println(progName +
": Missing rounds for \"-r\"");
usage = true;
} else {
try {
numRounds = Integer.parseInt(str);
} catch (NumberFormatException nfe) {
System.err.println(progName +
": Bad number of rounds \"" + str + "\"");
numRounds = 1;
usage = true;
}
if (numRounds < 1) {
System.err.println(progName +
": Need at least one round!");
usage = true;
}
}
break;
case 's':
str = (args[i].length() > 2 ? args[i].substring(2) :
((i + 1) < args.length ? args[++i] : null));
if (str == null) {
System.err.println(progName +
": Missing random seed value for \"-s\"");
usage = true;
} else {
try {
randSeed = Long.parseLong(str);
} catch (NumberFormatException nfe) {
System.err.println(progName +
": Bad random seed value \"" + str + "\"");
usage = true;
}
}
break;
case 'v':
verbose = true;
break;
default:
System.err.println(progName +
": Unknown option \"-" + ch + "\"");
usage = true;
break;
}
} else {
System.err.println(progName + ": Unknown keyword \"" + args[i] + "\"");
usage = true;
}
}
if (usage) {
System.err.println("Usage: " + getClass().getName() +
" [-c numClients]" +
" [-d delayUSecs]" +
" [-e numEvents]" +
" [-r numRounds]" +
" [-s randomSeed]" +
" [-v(erbose)]" +
"");
}
return !usage;
}
class Bundle
extends Thread
{
private DisplayImplJ2D dpy;
private TriggerControl tc;
private ColorAlphaControl cac;
public Bundle(DisplayImpl server)
throws RemoteException, VisADException
{
this(server, true);
}
public Bundle(DisplayImpl server, boolean buildClient)
throws RemoteException, VisADException
{
if (!buildClient) {
dpy = (DisplayImplJ2D )server;
ScalarMap map = new ScalarMap(mapType, Display.RGBA);
dpy.addMap(map);
} else {
RemoteDisplayImpl rmtdpy = new RemoteDisplayImpl(server);
dpy = new DisplayImplJ2D(rmtdpy);
}
tc = new TriggerControl(dpy);
dpy.addControl(tc);
ScalarMap map = findMap(dpy, Display.RGBA);
cac = (ColorAlphaControl )map.getControl();
start();
}
private ScalarMap findMap(LocalDisplay dpy, RealType displayScalar)
{
if (displayScalar == null) {
return null;
}
java.util.Iterator maps;
try {
maps = dpy.getMapVector().iterator();
} catch (Exception e) {
maps = null;
}
if (maps != null) {
while (maps.hasNext()) {
ScalarMap smap = (ScalarMap )maps.next();
if (displayScalar.equals(smap.getDisplayScalar())) {
return smap;
}
}
}
try {
maps = dpy.getConstantMapVector().iterator();
} catch (Exception e) {
maps = null;
}
if (maps != null) {
while (maps.hasNext()) {
ConstantMap cmap = (ConstantMap )maps.next();
if (displayScalar.equals(cmap.getDisplayScalar())) {
return cmap;
}
}
}
return null;
}
public DisplayImpl getDisplay() { return dpy; }
private float[][] getRandomTable(int w, int h)
{
float[][] t = new float[w][h];
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
t[i][j] = rand.nextFloat();
}
}
return t;
}
public BaseColorControl getColorControl() { return cac; }
public TriggerControl getTriggerControl() { return tc; }
public void run()
{
while (true) {
synchronized (waiter) {
try { waiter.wait(); } catch (InterruptedException ie) { }
}
try {
tc.fire();
cac.setTable(getRandomTable(cac.getNumberOfComponents(),
cac.getNumberOfColors()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
interface Revolver
{
void triggered();
}
private static int masterTriggerNum = 1;
class TriggerControl
extends Control
{
private ArrayList list;
private int num, val;
public TriggerControl(DisplayImpl dpy)
{
super(dpy);
list = new ArrayList();
num = masterTriggerNum++;
val = 0;
}
public void addListener(Revolver rvlvr)
{
synchronized (list) {
list.add(rvlvr);
}
}
private final void delay()
{
int dLen = rand.nextInt() % 50;
dLen = (dLen < 0 ? -dLen : (dLen == 0 ? 1 : dLen));
new Delay(dLen);
}
public void fire()
throws RemoteException, VisADException
{
val += num;
changeControl(true);
notifyListeners();
}
public String getSaveString() { return Integer.toString(val); }
public int getValue() { return val; }
public void notifyListeners()
{
ListIterator iter = list.listIterator();
while (iter.hasNext()) {
Revolver r = (Revolver )iter.next();
r.triggered();
}
}
public void setSaveString(String save)
throws VisADException, RemoteException
{
if (save == null) {
throw new VisADException("Invalid save string");
}
try {
val = Integer.parseInt(save);
} catch (NumberFormatException nfe) {
throw new VisADException("Bad TriggerControl save string \"" + save +
"\"");
}
}
public void syncControl(Control ctl)
throws VisADException
{
if (ctl == null) {
throw new VisADException("Cannot synchronize " +
this.getClass().getName() +
" with null Control object");
}
if (!(ctl instanceof TriggerControl)) {
throw new VisADException("Cannot synchronize " +
this.getClass().getName() +
" with " + ctl.getClass().getName());
}
TriggerControl tc = (TriggerControl )ctl;
boolean changed = false;
if (val != tc.val) {
changed = true;
val = tc.val;
notifyListeners();
}
if (changed) {
try {
changeControl(true);
} catch (RemoteException re) {
throw new VisADException("Could not indicate that control" +
" changed: " + re.getMessage());
}
}
}
public boolean equals(Object o)
{
if (!super.equals(o)) {
return false;
}
TriggerControl tc = (TriggerControl )o;
if (val != tc.val) {
return false;
}
return true;
}
public String toString()
{
StringBuffer buf = new StringBuffer("TriggerControl[#");
buf.append(num);
buf.append('=');
buf.append(val);
buf.append(']');
return buf.toString();
}
}
public static void main(String[] args)
{
new TestEvents(args);
}
}