예제 #1
0
 /*
  * (non-Javadoc)
  *
  * @see ch.elexis.data.cache.IPersistentObjectCache#stat()
  */
 public void stat() {
   long total = hits + misses + removed + expired;
   if (total != 0) {
     StringBuilder sb = new StringBuilder();
     sb.append("--------- cache statistics ------\n")
         .append("Total read:\t")
         .append(total)
         .append("\n")
         .append("cache hits:\t")
         .append(hits)
         .append(" (")
         .append(hits * 100 / total)
         .append("%)\n")
         .append("object expired:\t")
         .append(expired)
         .append(" (")
         .append(expired * 100 / total)
         .append("%)\n")
         .append("cache missed:\t")
         .append(misses)
         .append(" (")
         .append(misses * 100 / total)
         .append("%)\n")
         .append("object removed:\t")
         .append(removed)
         .append(" (")
         .append(removed * 100 / total)
         .append("%)\n")
         .append("Object inserts:\t")
         .append(inserts)
         .append("\n");
     log.log(sb.toString(), Log.INFOS);
   }
 }
예제 #2
0
 /**
  * Heartbeat (wieder) laufen lassen.
  *
  * @param immediately true: Sofort einen ersten beat losschicken, false: im normalen Rhythmus
  *     bleiben.
  */
 public void resume(boolean immediately) {
   isSuspended = false;
   log.log("resume", Log.DEBUGMSG); // $NON-NLS-1$
   if (immediately) {
     theBeat.run();
   }
 }
예제 #3
0
/**
 * A Cache with soft references and optional expiring items The cache keeps count on numbes of items
 * that are added, removed or expired and can display its statistic
 *
 * @author Gerry
 */
@SuppressWarnings("unchecked")
public class SoftCache<K> implements IPersistentObjectCache<K> {
  private static boolean enabled = true;
  protected Map<K, CacheEntry> cache;
  protected long hits, misses, removed, inserts, expired;
  protected Log log = Log.get("SoftCache");

  public SoftCache() {
    // must be thread-safe
    cache = Collections.synchronizedMap(new HashMap<K, CacheEntry>());
  }

  public SoftCache(final int num, final float load) {
    cache = Collections.synchronizedMap(new HashMap<K, CacheEntry>(num, load));
  }

  public SoftCache(final int num) {
    cache = Collections.synchronizedMap(new HashMap<K, CacheEntry>(num));
  }

  /*
   * (non-Javadoc)
   *
   * @see ch.elexis.data.cache.IPersistentObjectCache#put(K, java.lang.Object, int)
   */
  public void put(final K key, final Object object, final int timeToCacheInSeconds) {
    if (enabled) {
      cache.put(key, new CacheEntry(object, timeToCacheInSeconds));
      inserts++;
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see ch.elexis.data.cache.IPersistentObjectCache#get(K)
   */
  public Object get(final K key) {
    if (!enabled) {
      return null;
    }
    synchronized (cache) {
      CacheEntry ref = cache.get(key);
      if (ref == null) {
        misses++;
        return null;
      }
      Object ret = ref.get();
      if (ret == null) {
        remove(key);
        return null;
      } else {
        hits++;
        return ret;
      }
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see ch.elexis.data.cache.IPersistentObjectCache#remove(K)
   */
  public void remove(final K key) {
    synchronized (cache) {
      cache.remove(key);
      removed++;
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see ch.elexis.data.cache.IPersistentObjectCache#clear()
   */
  public void clear() {
    synchronized (cache) {
      purge();
      cache.clear();
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see ch.elexis.data.cache.IPersistentObjectCache#stat()
   */
  public void stat() {
    long total = hits + misses + removed + expired;
    if (total != 0) {
      StringBuilder sb = new StringBuilder();
      sb.append("--------- cache statistics ------\n")
          .append("Total read:\t")
          .append(total)
          .append("\n")
          .append("cache hits:\t")
          .append(hits)
          .append(" (")
          .append(hits * 100 / total)
          .append("%)\n")
          .append("object expired:\t")
          .append(expired)
          .append(" (")
          .append(expired * 100 / total)
          .append("%)\n")
          .append("cache missed:\t")
          .append(misses)
          .append(" (")
          .append(misses * 100 / total)
          .append("%)\n")
          .append("object removed:\t")
          .append(removed)
          .append(" (")
          .append(removed * 100 / total)
          .append("%)\n")
          .append("Object inserts:\t")
          .append(inserts)
          .append("\n");
      log.log(sb.toString(), Log.INFOS);
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see ch.elexis.data.cache.IPersistentObjectCache#purge()
   */
  public void purge() {
    synchronized (cache) {
      Iterator<Entry<K, CacheEntry>> it = cache.entrySet().iterator();
      long freeBefore = Runtime.getRuntime().freeMemory();
      while (it.hasNext()) {
        Entry<K, CacheEntry> e = it.next();
        CacheEntry ce = e.getValue();
        ce.expires = 0;
        ce.get();
        it.remove();
      }
      if (Hub.plugin.DEBUGMODE) {
        long freeAfter = Runtime.getRuntime().freeMemory();
        StringBuilder sb = new StringBuilder();
        sb.append("Cache purge: Free memore before: ")
            .append(freeBefore)
            .append(", free memory after: ")
            .append(freeAfter)
            .append("\n");
        Hub.log.log(sb.toString(), Log.INFOS);
      }
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see ch.elexis.data.cache.IPersistentObjectCache#reset()
   */
  public synchronized void reset() {
    purge();
    cache.clear();
  }

  public class CacheEntry extends SoftReference {
    long expires;

    public CacheEntry(final Object obj, final int timeInSeconds) {
      super(obj);
      expires = System.currentTimeMillis() + timeInSeconds * 1000;
    }

    @Override
    public synchronized Object get() {
      Object ret = super.get();
      if (System.currentTimeMillis() > expires) {
        expired++;
        super.clear();
        ret = null;
      }
      return ret;
    }
  }
}
예제 #4
0
 /** Heartbeat aussetzen (geht im Hintergrund weiter, wird aber nicht mehr weitergeleitet) */
 public void suspend() {
   log.log("suspending", Log.DEBUGMSG); // $NON-NLS-1$
   isSuspended = true;
 }
예제 #5
0
/**
 * Heartbeat is an event source, that fires events at user-definable intervals to all
 * HeartListeners. All actions that must be repeated regularly should be registered as
 * HeartListener. They will all be called at about the specified rate, but not in a guaranteed
 * particular order and not necessarily at exactly identical intervals.
 *
 * <p>Heartbeat löst das Pinger-Konzept ab. Der Heartbeat ist ein Singleton, das alle
 * Hub.localCfg.get(heartbeatrate,30) Sekunden einen Event feuert. Wer reglmässige Aktionen
 * durchführen will, kann sich als HeartbeatListener registrieren. Dieses Konzept hat gegenüber
 * individuellen update-Threads den Vorteil, dass die Netzwerk- und Datenbankbelastung, sowie die
 * Zahl der gleichzeitig laufenden Threads limitiert wird. Der Heartbeat sorgt dafür, dass die
 * listener der Reihe nach (aber ncht in einer definierten Reihenfolge) aufgerufen werden.
 *
 * <p>The client registering a listener can define the frequency, whether the listener should be
 * called at every single heart beat or at a lower frequency.
 *
 * @author gerry
 */
public class Heartbeat {
  /** Registering a listener using FREQUENCY_HIGH, it is called at every single heartbeat. */
  public static final int FREQUENCY_HIGH = 1;
  /** Registering a listener using FREQUENCY_MEDIUM, it is called at every 4th heartbeat. */
  public static final int FREQUENCY_MEDIUM = 2;
  /** Registering a listener using FREQUENCY_LOW, it is called at every 16th heartbeat. */
  public static final int FREQUENCY_LOW = 3;

  private beat theBeat;
  private Timer pacer;
  private boolean isSuspended;
  private static Heartbeat theHeartbeat;
  private CopyOnWriteArrayList<HeartListener> highFrequencyListeners;
  private CopyOnWriteArrayList<HeartListener> mediumFrequencyListeners;
  private CopyOnWriteArrayList<HeartListener> lowFrequencyListeners;
  private static Log log = Log.get("Heartbeat"); // $NON-NLS-1$

  private Heartbeat() {
    theBeat = new beat();
    highFrequencyListeners = new CopyOnWriteArrayList<HeartListener>();
    mediumFrequencyListeners = new CopyOnWriteArrayList<HeartListener>();
    lowFrequencyListeners = new CopyOnWriteArrayList<HeartListener>();
    pacer = new Timer(true);
    int interval = Hub.localCfg.get(PreferenceConstants.ABL_HEARTRATE, 30); // $NON-NLS-1$
    isSuspended = true;
    pacer.schedule(theBeat, 0, interval * 1000L);
  }

  /**
   * Das Singleton holen
   *
   * @return den Heartbeat der Anwendung
   */
  public static Heartbeat getInstance() {
    if (theHeartbeat == null) {
      theHeartbeat = new Heartbeat();
    }
    return theHeartbeat;
  }

  /**
   * Heartbeat (wieder) laufen lassen.
   *
   * @param immediately true: Sofort einen ersten beat losschicken, false: im normalen Rhythmus
   *     bleiben.
   */
  public void resume(boolean immediately) {
    isSuspended = false;
    log.log("resume", Log.DEBUGMSG); // $NON-NLS-1$
    if (immediately) {
      theBeat.run();
    }
  }

  /** Heartbeat aussetzen (geht im Hintergrund weiter, wird aber nicht mehr weitergeleitet) */
  public void suspend() {
    log.log("suspending", Log.DEBUGMSG); // $NON-NLS-1$
    isSuspended = true;
  }

  /** Heartbeat stoppen (kann dann nicht mehr gestartet werden) */
  public void stop() {
    log.log("stopping", Log.DEBUGMSG); // $NON-NLS-1$
    pacer.cancel();
  }

  /**
   * Einen Listener registrieren. Achtung: Muss unbedingt mit removeListener deregistriert werden
   * Calls addListener(listen, FREQUENCY_HIGH)
   *
   * @param listen der Listener
   */
  public void addListener(HeartListener listen) {
    addListener(listen, FREQUENCY_HIGH);
  }

  /**
   * Add listener using the specified frequency. Must be de-regsitered again using removeListener
   *
   * @param listener
   * @param frequency the frequency to call this listener. One of FREQUENCY_HIGH, FREQUENCY_MEDIUM,
   *     FREQUENCY_LOW
   */
  public void addListener(HeartListener listen, int frequency) {
    if (!highFrequencyListeners.contains(listen)
        && !mediumFrequencyListeners.contains(listen)
        && !lowFrequencyListeners.contains(listen)) {

      switch (frequency) {
        case FREQUENCY_HIGH:
          highFrequencyListeners.add(listen);
          break;
        case FREQUENCY_MEDIUM:
          mediumFrequencyListeners.add(listen);
          break;
        case FREQUENCY_LOW:
          lowFrequencyListeners.add(listen);
          break;
      }
    }
  }

  /**
   * Einen Listener wieder austragen
   *
   * @param listen
   */
  public void removeListener(HeartListener listen) {
    // remove the listener from the three lists
    // actually, it's contained in only one of them, but we don't know which one
    highFrequencyListeners.remove(listen);
    mediumFrequencyListeners.remove(listen);
    lowFrequencyListeners.remove(listen);
  }

  /**
   * we beat asynchronously, because most listeners will update their views
   *
   * @author Gerry
   */
  private class beat extends TimerTask {
    private static final int FREQUENCY_HIGH_MULTIPLIER = 1;
    private static final int FREQUENCY_MEDIUM_MULTIPLIER = 4;
    private static final int FREQUENCY_LOW_MULTIPLIER = 16;

    // multiplier for resetting counter after each round
    private static final int RESET_MULTIPLIER = FREQUENCY_LOW_MULTIPLIER;

    private int counter = 0;

    @Override
    public void run() {
      if (!isSuspended) {
        Desk.getDisplay()
            .asyncExec(
                new Runnable() {
                  public void run() {
                    // low frequency
                    if (counter % FREQUENCY_LOW_MULTIPLIER == 0) {
                      log.log("Heartbeat low", Log.DEBUGMSG); // $NON-NLS-1$
                      for (HeartListener l : lowFrequencyListeners) {
                        l.heartbeat();
                      }
                    }
                    // medium frequency
                    if (counter % FREQUENCY_MEDIUM_MULTIPLIER == 0) {
                      log.log("Heartbeat medium", Log.DEBUGMSG); // $NON-NLS-1$
                      for (HeartListener l : mediumFrequencyListeners) {
                        l.heartbeat();
                      }
                    }
                    // high frequency
                    if (counter % FREQUENCY_HIGH_MULTIPLIER == 0) {
                      log.log("Heartbeat high", Log.DEBUGMSG); // $NON-NLS-1$
                      for (HeartListener l : highFrequencyListeners) {
                        l.heartbeat();
                      }
                    }
                  }
                });
      }
      counter++;
      counter %= RESET_MULTIPLIER;
    }
  }

  public interface HeartListener {
    /**
     * Die Methode heartbeat wird in "einigermassen" regelmässigen (aber nicht garantiert immer
     * genau identischen) Abständen aufgerufen
     */
    public void heartbeat();
  }
}
예제 #6
0
 /** Heartbeat stoppen (kann dann nicht mehr gestartet werden) */
 public void stop() {
   log.log("stopping", Log.DEBUGMSG); // $NON-NLS-1$
   pacer.cancel();
 }