/*
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
*/
package visad.collab;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;
import visad.Control;
import visad.RemoteDisplay;
import visad.RemoteVisADException;
import visad.util.ThreadPool;
class MonitorSyncer
implements Runnable
{
private String Name;
private boolean dead = false;
private Object cacheLock = new Object();
private Thread thisThread = null;
private ArrayList current = new ArrayList();
private ArrayList diverted = null;
private HashMap eventCache = null;
private RemoteDisplay rmtDpy;
private MonitorCallback callback;
private int id;
private RemoteEventProvider provider;
/**
* The event callback thread pool and its lock.
*/
private transient static ThreadPool callbackPool = null;
private static Object callbackPoolLock = new Object();
public MonitorSyncer(String name, MonitorCallback callback, int id)
throws RemoteException
{
this.Name = name + ":MonL";
this.eventCache = new HashMap();
this.rmtDpy = null;
this.callback = callback;
this.id = id;
this.provider = new RemoteEventProviderImpl(this);
}
public MonitorSyncer(String name, RemoteDisplay rmtDpy, int id)
throws RemoteException
{
this.Name = name + ":MonL";
this.eventCache = new HashMap();
this.rmtDpy = rmtDpy;
this.callback = rmtDpy.getRemoteDisplaySync();
this.id = id;
this.provider = new RemoteEventProviderImpl(this);
}
public void addEvent(MonitorEvent evt)
{
String key = evt.getKey();
synchronized (cacheLock) {
MonitorEvent oldEvt = (MonitorEvent )eventCache.put(key, evt);
if (thisThread != null) {
if (diverted == null) {
diverted = new ArrayList();
}
diverted.add(key);
} else {
current.add(key);
thisThread = new Thread(this);
thisThread.start();
}
}
}
public MonitorEvent getEvent(Object key)
{
MonitorEvent evt;
synchronized (cacheLock) {
evt = (MonitorEvent )eventCache.remove(key);
}
// mark message as coming from this connection, so we don't see it again
if (evt != null) {
evt.setOriginator(id);
}
return evt;
}
/**
* Get the unique identifier.
*
* @return the unique identifier.
*/
public int getID() { return id; }
public MonitorCallback getListener() { return callback; }
public String getName() { return Name; }
public boolean hasControlEventQueued(Control ctl)
{
if (ctl == null) {
return false;
}
return eventCache.containsKey(ControlMonitorEvent.getControlKey(ctl));
}
// WLH 12 April 2001
public boolean isEmpty() {
return eventCache.isEmpty();
}
/**
* Check to see if the connection is dead.
*
* @return <TT>true</TT> if the connection is dead.
*/
public boolean isDead() { return dead; }
/**
* Check to see if this object is monitoring the specified
* <tt>RemoteDisplay</tt>.
*
* @param rmtDpy <tt>RemoteDisplay</tt> being searched for.
*
* @return <tt>true</tt> if this object is monitoring the display.
*/
public boolean isMonitored(RemoteDisplay rmtDpy)
{
return this.rmtDpy.equals(rmtDpy);
}
public void run()
{
boolean done = false;
try {
int attempts = 0;
while (!done) {
try {
sendEventKeys(current);
done = true;
} catch (RemoteException re) {
if (attempts++ < 5) {
// wait a bit, then try again to notify the remote Display
try { Thread.sleep(500); } catch (InterruptedException ie) { }
} else {
// if we failed to connect for 10 times, give up
dead = true;
break;
}
} catch (RemoteVisADException rve) {
rve.printStackTrace();
done = true;
}
if (done) {
synchronized (cacheLock) {
if (!undivertEvents()) {
break;
}
done = false;
}
}
}
} finally {
// indicate that the thread has exited
synchronized (cacheLock) {
thisThread = null;
}
}
}
private void sendEventKeys(ArrayList list)
throws RemoteException, RemoteVisADException
{
while (list.size() > 0) {
String key = (String )list.remove(0);
callback.eventReady(provider, key);
}
}
public String toString()
{
StringBuffer buf = new StringBuffer("MonitorSyncer[");
buf.append(Name);
buf.append("=#");
buf.append(id);
buf.append(']');
return buf.toString();
}
/**
* Returns <TT>true</TT> if there were diverted requests.
*/
private boolean undivertEvents()
{
final boolean undivert;
synchronized (cacheLock) {
// if there are events queued, restore them to the main table
undivert = (diverted != null);
if (undivert) {
current = diverted;
diverted = null;
}
}
return undivert;
}
/**
* Used as key for ControlEvents in listener queue
*/
class ControlEventKey
{
private Control control;
private Class cclass;
private int instance;
ControlEventKey(Control ctl)
{
control = ctl;
cclass = ctl.getClass();
instance = ctl.getInstanceNumber();
}
public boolean equals(ControlEventKey key)
{
return instance == key.instance && cclass.equals(key.cclass);
}
public boolean equals(Object obj)
{
if (!(obj instanceof ControlEventKey)) {
return false;
}
return equals((ControlEventKey )obj);
}
public boolean equals(Control ctl)
{
return cclass.equals(ctl.getClass());
}
public int hashCode()
{
return cclass.hashCode() + instance;
}
public String toString()
{
StringBuffer buf = new StringBuffer(control.toString());
if (buf.length() > 48) {
buf.setLength(0);
}
buf.insert(0, ':');
buf.insert(0, instance);
buf.insert(0, '#');
buf.insert(0, cclass.getName());
return buf.toString();
}
}
}