/* * AbstractDataCollector.java jchart2d Copyright (C) 2004 - 2011 Achim Westermann, created * on 10.12.2004, 14:48:09 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * If you modify or optimize the code in a useful way please let me know. * Achim.Westermann@gmx.de */ package info.monitorenter.gui.chart.io; import info.monitorenter.gui.chart.ITrace2D; import info.monitorenter.gui.chart.ITracePoint2D; /** * A simple Runnable that continuously collects data every latency time period * and adds it to the internal ITrace2D instance. * <p> * Extend from this class and override the method {@link #collectData()}. * <p> * Set it up with code like: * * <pre> * Chart2D chart = new Chart2D(); * ITrace2D trace = <initialization> * chart.addTrace(trace); * // Put the chart in your UI... * // ... * AbstractDataCollector collector = new <subtypename>(200,trace); * collector.start(); * </pre> * <p> * * <h3>Caution</h3> * Calling <code>new Thread(collector).start()</code> is disallowed and will * throw an exception as it would allow several Threads to run a collector. Use * the {@link #start()} instead. * <p> * <b>Always connect the trace to a chart first before starting the collector for that trace!</b> * (deadlock prevention will raise an exception else). * <p> * * @author <a href="mailto:Achim.Westermann@gmx.de">Achim Westermann </a> * @version $Revision: 1.11 $ */ public abstract class ADataCollector implements Runnable { /** Flag to check wether the collector is running. */ private boolean m_isRunning = false; /** The interval for data collection of a single point. */ private long m_latency = 400; /** This flag controls stopping / starting the thread that is used. */ private boolean m_stop = true; /** * The thread that is created in {@link #start()}. * <p> * If someone tries to: <code>new Thread(collector).start()</code> instead * of <code>collector.start()</code> an exception will be thrown. */ private Thread m_thread; /** The trace to add collected data to. */ private ITrace2D m_trace; /** * Creates an instance that will collect every latency ms a point and add it * to the trace. * <p> * * @param trace * the trace to add collected points to. * @param latency * the interval in ms for collecting points. */ public ADataCollector(final ITrace2D trace, final long latency) { super(); this.m_latency = latency; this.m_trace = trace; } /** * <p> * Override this method. It will be invoked in intervals of the configured * latency time. The TracePoint2D that is returned will be added to the * constructor given ITrace2D. * </p> * <p> * Keep your implementation fast. If the computations performed here take * longer than the latency time that desired refresh rate will not be reached. * </p> * * @return the collected point. */ public abstract ITracePoint2D collectData(); /** * @see java.lang.Object#finalize() */ @Override protected void finalize() throws Throwable { super.finalize(); this.stop(); } /** * Returns the interval in ms a point is collected. * <p> * * @return the interval in ms a point is collected. */ public long getLatency() { return this.m_latency; } /** * Returns the trace that is filled by this collector. * <p> * * @return Returns the trace. */ public ITrace2D getTrace() { return this.m_trace; } /** * Returns true if this datacollector currently is running. * <p> * * @return true if this datacollector currently is running. */ public boolean isRunning() { return this.m_isRunning; } /** * @see java.lang.Runnable#run() */ public void run() { if (Thread.currentThread() != this.m_thread) { throw new IllegalStateException( "You cannot start an own thread for data collectors. Use collector.start()!"); } this.m_isRunning = true; long lasttime; this.m_stop = false; ITracePoint2D point; while (!this.m_stop) { lasttime = System.currentTimeMillis(); point = this.collectData(); this.m_trace.addPoint(point); try { Thread.sleep(Math.max(this.m_latency - System.currentTimeMillis() + lasttime, 0)); } catch (InterruptedException e) { this.stop(); } if (Thread.interrupted()) { this.stop(); } } this.m_isRunning = false; } /** * Sets the interval for collecting points in ms. * <p> * * @param latency * the interval for collecting points in ms. */ public void setLatency(final long latency) { this.m_latency = latency; } /** * <p> * Starts a Thread using this {@link Runnable}. * </p> * <p> * This method will not start a new Thread if the current one is still * running. If you prefer to use your own Threads (e.g. from a ThreadPool) * prefer: * * <pre> * AbstractDataCollector collector = new <subtypename>(200,trace); * new Thread(collector).start(); * </pre> * * or more abstract (as proposed for Thread improvement reasons: * * <pre> * AbstractDataCollector collector = new <subtypename>(200,trace); * <getSomeThreadInstance>(collector).start(); * </pre> * * </p> */ public void start() { if (this.m_stop) { this.m_thread = new Thread(this); this.m_thread.start(); } } /** * Stops this Thread. Data collection will end when finished the current loop. * <p> * Note that your application may * <ol> * <li>run into deadlocks (blocking IO,...) * <li>face memory problems * </ol> * if the AbstractDataCollector implementation fetches and removes data from * 1) a limited buffer or a 2) unlimited buffer. This behaviour will of course * not appear if the data is not read from a queue where it has to be removed * from. * <p> */ public void stop() { this.m_stop = true; } }