/*
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.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Vector;

import visad.ConstantMap;
import visad.Control;
import visad.DataRenderer;
import visad.DisplayImpl;
import visad.RemoteDataReference;
import visad.RemoteDisplayImpl;
import visad.RemoteReferenceLink;
import visad.RemoteVisADException;
import visad.ScalarMap;
import visad.VisADException;

public class DisplaySyncImpl
  implements Comparator, DisplaySync, Runnable
{
  private String Name;
  private DisplayImpl myDisplay;
  private DisplayMonitor monitor;

  private Object mapClearSync = new Object();
  private int mapClearCount = 0;

  private boolean dead = false;

  private Object tableLock = new Object();
  private Thread thisThread = null;

  private HashMap current = new HashMap();
  private HashMap diverted = null;

  public DisplaySyncImpl(DisplayImpl dpy)
    throws RemoteException
  {
    Name = dpy.getName() + ":Sync";
    myDisplay = dpy;
    monitor = dpy.getDisplayMonitor();
    monitor.setDisplaySync(this);
  }

  /**
   * Adds the specified data reference to this Display.
   *
   * @param link The link to the remote data reference.
   *
   * @exception VisADException If a link could not be made for the
   * 				remote data reference.
   */
  private void addLink(RemoteReferenceLink link)
    throws VisADException
  {
    // build array of ConstantMap values
    ConstantMap[] cm = null;
    try {
      Vector v = link.getConstantMapVector();
      int len = v.size();
      if (len > 0) {
        cm = new ConstantMap[len];
        for (int i = 0; i < len; i++) {
          ConstantMap tmp = (ConstantMap )v.elementAt(i);
          cm[i] = (ConstantMap )tmp.clone();
        }
      }
    } catch (Exception e) {
      throw new VisADException("Couldn't copy ConstantMaps" +
                               " for remote DataReference");
    }

    // get reference to Data object
    RemoteDataReference ref;
    try {
      ref = link.getReference();
    } catch (Exception e) {
      throw new VisADException("Couldn't copy remote DataReference");
    }

    if (ref != null) {

      DataRenderer dr = myDisplay.getDisplayRenderer().makeDefaultRenderer();
      String defaultClass = dr.getClass().getName();

      // get proper DataRenderer
      DataRenderer renderer;
      try {
        String newClass = link.getRendererClassName();
        if (newClass == defaultClass) {
          renderer = null;
        } else {
          Object obj = Class.forName(newClass).newInstance();
          renderer = (DataRenderer )obj;
        }
      } catch (Exception e) {
        throw new VisADException("Couldn't copy remote DataRenderer " +
                                 "name; using " + defaultClass);
      }

      // build RemoteDisplayImpl to which reference is attached
      try {
        RemoteDisplayImpl rd = new RemoteDisplayImpl(myDisplay);

        // if this reference uses the default renderer...
        if (renderer == null) {
          rd.addReference(ref, cm);
        } else {
          rd.addReferences(renderer, ref, cm);
        }
      } catch (Exception e) {
        e.printStackTrace();
        throw new VisADException("Couldn't add remote DataReference " +
                                 ref + ": " + e.getClass().getName() +
                                 ": " + e.getMessage());
      }
    }
  }

  public int compare(Object o1, Object o2)
  {
    return (((MonitorEvent )o1).getSequenceNumber() -
            ((MonitorEvent )o2).getSequenceNumber());
  }

  public void destroy()
  {
    monitor = null;
    myDisplay = null;
  }
 
  /**
   * Finds the first map associated with this Display
   * which matches the specified ScalarMap.
   *
   * @param map The ScalarMap to find.
   */
  private ScalarMap findMap(ScalarMap map)
  {
    ScalarMap found = null;

    boolean isConstMap;
    Vector v;
    if (map instanceof ConstantMap) {
      v = myDisplay.getConstantMapVector();
      isConstMap = true;
    } else {
      v = myDisplay.getMapVector();
      isConstMap = false;
    }

    ListIterator iter = v.listIterator();
    while (iter.hasNext()) {
      ScalarMap sm = (ScalarMap )iter.next();
      if (sm.equals(map)) {
        found = sm;
        break;
      }
    }

    return found;
  }

  /**
   * Start event callback.
   */
  public void eventReady(RemoteEventProvider provider, Object key)
  {
    synchronized (tableLock) {
      if (thisThread != null) {
        if (diverted == null) {
          diverted = new HashMap();
        }
        diverted.put(key, provider);
      } else {
        current.put(key, provider);
        thisThread = new Thread(this);
        thisThread.start();
      }
    }
  }

  public String getName() { return Name; }

  public boolean isLocalClear()
  {
    boolean result = true;
    synchronized (mapClearSync) {
      if (mapClearCount > 0) {
        mapClearCount--;
        result = false;
      }
    }

    return result;
  }

  public boolean isThreadRunning()
  {
    return (thisThread != null);
  }

  private void processMap(HashMap map)
  {
    MonitorEvent[] list = new MonitorEvent[map.size()];

    // build the array of events
    Iterator iter = map.keySet().iterator();
    for (int i = list.length - 1; i >= 0; i--) {
      if (iter.hasNext()) {
        String key = (String )iter.next();
        list[i] = (MonitorEvent )map.get(key);
      } else {
        list[i] = null;
      }
    }

    // sort events by order of creation
    Arrays.sort(list, this);

    int i, attempts;
    i = attempts = 0;
    while (i < list.length) {
      try {
        processOneEvent(list[i]);
        i++;
      } catch (RemoteException re) {
        if (attempts++ < 5) {
          // wait a bit, then try again to request the events
          try { Thread.sleep(500); } catch (InterruptedException ie) { }
        } else {
          // if we failed to connect for 10 times, give up
          dead = true;
          break;
        }
      } catch (RemoteVisADException rve) {
        System.err.println("While processing " + list[i] + ":");
        i++;
        rve.printStackTrace();
      }
    }
  }

  private void processOneEvent(MonitorEvent evt)
    throws RemoteException, RemoteVisADException
  {
    Control lclCtl, rmtCtl;
    ScalarMap lclMap, rmtMap;

    switch (evt.getType()) {
    case MonitorEvent.MAP_ADDED:

      rmtMap = ((MapMonitorEvent )evt).getMap();

      // if we haven't already added this map...
      if (findMap(rmtMap) == null) {
/* WLH 26 Dec 2002
        if (!myDisplay.getRendererVector().isEmpty()) {
          System.err.println("Late addMap: " + rmtMap);
        } else {
*/
          try {
            myDisplay.addMap(rmtMap, evt.getOriginator());
          } catch (VisADException ve) {
            ve.printStackTrace();
            throw new RemoteVisADException("Map " + rmtMap + " not added: " +
                                           ve);
          }
/*
        }
*/
      }
      break;
    case MonitorEvent.MAP_REMOVED:

      rmtMap = ((MapMonitorEvent )evt).getMap();

      // if we have already added this map...
      if (findMap(rmtMap) != null) {
          try {
            myDisplay.removeMap(rmtMap, evt.getOriginator());
          } catch (VisADException ve) {
            ve.printStackTrace();
            throw new RemoteVisADException("Map " + rmtMap + " not removed: " +
                                           ve);
          }
      }
      break;
    case MonitorEvent.MAP_CHANGED:
      rmtMap = ((MapMonitorEvent )evt).getMap();
      lclMap = findMap(rmtMap);
      if (lclMap == null) {
        throw new RemoteVisADException("ScalarMap " + rmtMap + " not found");
      }
      // CTR 2 June 2000 - do not set map range if already set (avoid loops)
      double[] rng = rmtMap.getRange();
      double[] lclRng = lclMap.getRange();
      if (rng[0] != lclRng[0] || rng[1] != lclRng[1]) {
        try {
          lclMap.setRange(rng[0], rng[1], evt.getOriginator());
        } catch (VisADException ve) {
          throw new RemoteVisADException("Map not changed: " + ve);
        }
      }
      break;
    case MonitorEvent.MAPS_CLEARED:
      try {
        myDisplay.removeAllReferences();
        myDisplay.clearMaps();
      } catch (VisADException ve) {
        throw new RemoteVisADException("Maps not cleared: " + ve);
      } catch (NullPointerException npe) {
        npe.printStackTrace();
        throw new RemoteVisADException("Maps not cleared");
      }
      break;
    case MonitorEvent.REFERENCE_ADDED:
      RemoteReferenceLink ref = ((ReferenceMonitorEvent )evt).getLink();
      try {
        addLink(ref);
      } catch (VisADException ve) {
        throw new RemoteVisADException("DataReference " + ref +
                                       " not found by " + Name + ": " +
                                       ve.getMessage());
      }

      break;
    case MonitorEvent.CONTROL_INIT_REQUESTED:
      // !!! DON'T FORWARD INIT EVENTS TO LISTENERS !!!

      rmtCtl = ((ControlMonitorEvent )evt).getControl();
      lclCtl = myDisplay.getControl(rmtCtl.getClass(),
                                    rmtCtl.getInstanceNumber());
      if (lclCtl == null) {
        // didn't find control ... maybe it doesn't exist yet?
        break;
      }

      try {
        ControlMonitorEvent cme;
        cme = new ControlMonitorEvent(MonitorEvent.CONTROL_CHANGED,
                                      (Control )lclCtl.clone());
        monitor.notifyListeners(cme);
      } catch (VisADException ve) {
        throw new RemoteVisADException("Control " + rmtCtl +
                                       " not changed by " + Name + ": " + ve);
      }
      break;
    case MonitorEvent.CONTROL_CHANGED:
      rmtCtl = ((ControlMonitorEvent )evt).getControl();
      lclCtl = myDisplay.getControl(rmtCtl.getClass(),
                                    rmtCtl.getInstanceNumber());

      // skip this if we have change events to deliver for this control
      if (lclCtl != null &&
          !monitor.hasEventQueued(evt.getOriginator(), lclCtl))
      {

        try {
          lclCtl.syncControl(rmtCtl);
        } catch (VisADException ve) {
          throw new RemoteVisADException("Control " + lclCtl +
                                         " not changed by " + Name + ": " +
                                         ve.getMessage());
        }
      }

      break;
    case MonitorEvent.MESSAGE_SENT:
      myDisplay.sendMessage(((MessageMonitorEvent )evt).getMessage());
      break;
    default:
      throw new RemoteVisADException("Event " + evt + " not handled");
    }
  }

  private HashMap requestEventTable(HashMap table)
    throws RemoteException
  {
    HashMap map = null;

    Iterator iter = table.keySet().iterator();
    while (iter.hasNext()) {
      String key = (String )iter.next();
      RemoteEventProvider provider = (RemoteEventProvider )table.get(key);
      iter.remove();

      MonitorEvent evt = requestOneEvent(key, provider);
      if (evt != null) {
        if (map == null) {
          map = new HashMap();
        }
        map.put(key, evt);
      }
    }

    return map;
  }

  private MonitorEvent requestOneEvent(String key,
                                       RemoteEventProvider provider)
    throws RemoteException
  {
    // get the event
    MonitorEvent evt;
    try {
      evt = provider.getEvent(key);
    } catch (RemoteVisADException rve) {
      rve.printStackTrace();
      throw new RemoteException(rve.getMessage());
    }

    if (evt == null) {
      // if it's already been picked up, we're done
      return null;
    }

    switch (evt.getType()) {
    case MonitorEvent.MAPS_CLEARED:
      synchronized (mapClearSync) {
        mapClearCount++;
      }
      break;
    case MonitorEvent.CONTROL_CHANGED:
      boolean result;
      try {
        result = monitor.hasEventQueued(evt.getOriginator(),
                                        ((ControlMonitorEvent )evt).getControl());
      } catch (RemoteException re) {
        re.printStackTrace();
        result = false;
      }

      if (result) {
        // drop this event since we're about to override it
        return null;
      }
      break;
    }

    return evt;
  }

  /**
   * Requests events from the remote provider(s).
   */
  public void run()
  {
    HashMap map = null;

    boolean done = false;
    try {

      int attempts = 0;
      while (!done) {
        HashMap newMap;
        try {
          newMap = requestEventTable(current);
          done = true;
        } catch (RemoteException re) {
          if (attempts++ < 5) {
            // wait a bit, then try again to request the events
            try { Thread.sleep(500); } catch (InterruptedException ie) { }
            newMap = null;
          } else {
            // if we failed to connect for 10 times, give up
            dead = true;
            break;
          }
        }

        if (map == null) {
          map = newMap;
        } else if (newMap != null) {
          map.putAll(newMap);
        }

        if (done) {
          synchronized (tableLock) {
            if (!undivertEvents()) {
              break;
            }

            done = false;
          }
        }
      }
    } finally {

      if (map != null) {
        processMap(map);
      }

      // indicate that the thread has exited
      synchronized (tableLock) {
        thisThread = null;
      }
    }
  }

  /**
   * Returns true if there were diverted requests.
   */
  private boolean undivertEvents()
  {
    final boolean undivert;
    synchronized (tableLock) {
      // if there are events queued, restore them to the main table
      undivert = (diverted != null);
      if (undivert) {
        current = diverted;
        diverted = null;
      }
    }

    return undivert;
  }
}

 

Ads help maintain this website.