View Javadoc
1   /*
2    * Licensed under the GPL License. You may not use this file except in compliance with the License.
3    * You may obtain a copy of the License at
4    *
5    *   https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
6    *
7    * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
8    * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
9    * PURPOSE.
10   */
11  package psiprobe.beans.stats.collectors;
12  
13  import jakarta.inject.Inject;
14  
15  import java.util.List;
16  import java.util.Map;
17  import java.util.TreeMap;
18  
19  import org.jfree.data.xy.XYDataItem;
20  
21  import psiprobe.Utils;
22  import psiprobe.beans.stats.listeners.StatsCollectionEvent;
23  import psiprobe.beans.stats.listeners.StatsCollectionListener;
24  import psiprobe.model.stats.StatsCollection;
25  
26  /**
27   * The Class AbstractStatsCollectorBean.
28   */
29  public abstract class AbstractStatsCollectorBean {
30  
31    /** The stats collection. */
32    @Inject
33    protected StatsCollection statsCollection;
34  
35    /** The max series. */
36    private int maxSeries = 240;
37  
38    /** The listeners. */
39    private List<StatsCollectionListener> listeners;
40  
41    /** The previous data. */
42    private final Map<String, Long> previousData = new TreeMap<>();
43  
44    /** The previous data2 d. */
45    private final Map<String, Entry> previousData2D = new TreeMap<>();
46  
47    /**
48     * Gets the max series.
49     *
50     * @return the max series
51     */
52    public int getMaxSeries() {
53      return maxSeries;
54    }
55  
56    /**
57     * Sets the max series.
58     *
59     * @param maxSeries the new max series
60     */
61    public void setMaxSeries(int maxSeries) {
62      this.maxSeries = maxSeries;
63    }
64  
65    /**
66     * Gets the listeners.
67     *
68     * @return the listeners
69     */
70    public List<StatsCollectionListener> getListeners() {
71      return listeners;
72    }
73  
74    /**
75     * Sets the listeners.
76     *
77     * @param listeners the new listeners
78     */
79    public void setListeners(List<StatsCollectionListener> listeners) {
80      this.listeners = listeners;
81    }
82  
83    /**
84     * Collect.
85     *
86     * @throws Exception the exception
87     */
88    public abstract void collect() throws Exception;
89  
90    /**
91     * Builds the delta stats.
92     *
93     * @param name the name
94     * @param value the value
95     *
96     * @return the long
97     *
98     * @throws InterruptedException the interrupted exception
99     */
100   protected long buildDeltaStats(String name, long value) throws InterruptedException {
101     return buildDeltaStats(name, value, System.currentTimeMillis());
102   }
103 
104   /**
105    * Builds the delta stats.
106    *
107    * @param name the name
108    * @param value the value
109    * @param time the time
110    *
111    * @return the long
112    *
113    * @throws InterruptedException the interrupted exception
114    */
115   protected long buildDeltaStats(String name, long value, long time) throws InterruptedException {
116     long delta = 0;
117     if (statsCollection != null) {
118       long previousValue = Utils.toLong(previousData.get(name), 0);
119       delta = value - previousValue;
120       delta = delta > 0 ? delta : 0;
121       buildAbsoluteStats(name, delta, time);
122       previousData.put(name, value);
123     }
124     return delta;
125   }
126 
127   /**
128    * Builds the absolute stats.
129    *
130    * @param name the name
131    * @param value the value
132    *
133    * @throws InterruptedException the interrupted exception
134    */
135   protected void buildAbsoluteStats(String name, long value) throws InterruptedException {
136     buildAbsoluteStats(name, value, System.currentTimeMillis());
137   }
138 
139   /**
140    * Builds the absolute stats.
141    *
142    * @param name the name
143    * @param value the value
144    * @param time the time
145    *
146    * @throws InterruptedException the interrupted exception
147    */
148   protected void buildAbsoluteStats(String name, long value, long time)
149       throws InterruptedException {
150 
151     List<XYDataItem> stats = statsCollection.getStats(name);
152     if (stats == null) {
153       statsCollection.newStats(name, maxSeries);
154     } else {
155       XYDataItem data = new XYDataItem(time, value);
156       statsCollection.lockForUpdate();
157       try {
158         stats.add(data);
159         houseKeepStats(stats);
160       } finally {
161         statsCollection.releaseLock();
162       }
163       if (listeners != null) {
164         StatsCollectionEvent event = new StatsCollectionEvent(name, data);
165         for (StatsCollectionListener listener : listeners) {
166           if (listener.isEnabled()) {
167             listener.statsCollected(event);
168           }
169         }
170       }
171     }
172   }
173 
174   /**
175    * The Class Entry.
176    */
177   private static class Entry {
178 
179     /** The time. */
180     long time;
181 
182     /** The value. */
183     long value;
184 
185     /**
186      * Instantiates a new entry.
187      */
188     public Entry() {
189       // Prevent Emulation by Synthetic Accessor
190     }
191 
192   }
193 
194   /**
195    * If there is a value indicating the accumulated amount of time spent on something it is possible
196    * to build a series of values representing the percentage of time spent on doing something. For
197    * example:
198    *
199    * <p>
200    * at point T1 the system has spent A milliseconds performing tasks at point T2 the system has
201    * spent B milliseconds performing tasks
202    * </p>
203    *
204    * <p>
205    * so between in a timeframe T2-T1 the system spent B-A milliseconds being busy. Thus (B - A)/(T2
206    * - T1) * 100 is the percentage of all time the system spent being busy.
207    * </p>
208    *
209    * @param name the name of the value, which allows the collector to be used for multiple values
210    * @param value time spent on the task in milliseconds (A or B in the example above)
211    * @param time system time in milliseconds (T1 or T2 in the example above)
212    *
213    * @throws InterruptedException if a lock could not be obtained
214    */
215   protected void buildTimePercentageStats(String name, long value, long time)
216       throws InterruptedException {
217 
218     Entry entry = previousData2D.get(name);
219     if (entry == null) {
220       entry = new Entry();
221       entry.value = value;
222       entry.time = time;
223       previousData2D.put(name, entry);
224     } else {
225       double valueDelta = (double) value - entry.value;
226       double timeDelta = (double) time - entry.time;
227       double statValue = valueDelta * 100 / timeDelta;
228       statsCollection.lockForUpdate();
229       try {
230         List<XYDataItem> stats = statsCollection.getStats(name);
231         if (stats == null) {
232           stats = statsCollection.newStats(name, maxSeries);
233         }
234         stats.add(stats.size(), new XYDataItem(time, statValue));
235         houseKeepStats(stats);
236       } finally {
237         statsCollection.releaseLock();
238       }
239     }
240   }
241 
242   /**
243    * Reset stats.
244    *
245    * @param name the name
246    */
247   protected void resetStats(String name) {
248     statsCollection.resetStats(name);
249   }
250 
251   /**
252    * House keep stats.
253    *
254    * @param stats the stats
255    */
256   private void houseKeepStats(List<XYDataItem> stats) {
257     while (stats.size() > maxSeries) {
258       stats.remove(0);
259     }
260   }
261 }