View Javadoc

1   // Copyright (C) 2000 - 2012 Philip Aston
2   // Copyright (C) 2004 John Stanford White
3   // Copyright (C) 2004 Calum Fitzgerald
4   // All rights reserved.
5   //
6   // This file is part of The Grinder software distribution. Refer to
7   // the file LICENSE which is part of The Grinder distribution for
8   // licensing details. The Grinder distribution is available on the
9   // Internet at http://grinder.sourceforge.net/
10  //
11  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
12  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
13  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14  // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
15  // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
16  // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19  // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
20  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
22  // OF THE POSSIBILITY OF SUCH DAMAGE.
23  
24  package net.grinder.statistics;
25  
26  import static java.util.Arrays.asList;
27  
28  import java.io.Serializable;
29  import java.util.Collection;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Map;
33  
34  
35  /**
36   * A registry of statistic index objects.
37   *
38   * <p>
39   * Each statistic has a unique index object and a name. The index objects are
40   * used with {@link net.grinder.script.Statistics} and the names can be used in
41   * expressions (see {@link ExpressionView}). Statistics can either be
42   * <em>long</em> integer values (see {@link #getLongIndex}) or
43   * <em>double</em> floating-point values ({@link #getDoubleIndex}).
44   *
45   * <p>See {@link net.grinder.script.Statistics} for details of the statistics
46   * provided by The Grinder.
47   *
48   * <h4>Sample Statistics</h4>
49   *
50   * <p>
51   * There is a special type of index object for <em>sample</em> statistics, see
52   * {@link #getLongSampleIndex} and {@link #getDoubleSampleIndex}. Sample
53   * statistics are the result of a series of sample values. The values can be
54   * either <em>long</em>s or <em>double</em>s. Sample statistics have three
55   * attribute values that can be read: the <em>count</em> (number of samples),
56   * <em>sum</em> (total of all sample values), and sample <em>variance</em>.
57   * These attributes can be queried using the appropriate expression function
58   * (e.g. <em>count()</em>), see {@link ExpressionView}.
59   * </p>
60   *
61   * @author Philip Aston
62   */
63  public final class StatisticsIndexMap implements Serializable {
64  
65    private static final long serialVersionUID = 1;
66  
67    private final Map<String, DoubleIndex> m_doubleMap =
68      new HashMap<String, DoubleIndex>();
69    private final Map<String, LongIndex> m_longMap =
70      new HashMap<String, LongIndex>();
71    private final Map<String, LongIndex> m_transientLongMap =
72      new HashMap<String, LongIndex>();
73    private final Map<String, DoubleSampleIndex> m_doubleSampleMap =
74      new HashMap<String, DoubleSampleIndex>();
75    private final Map<String, LongSampleIndex> m_longSampleMap =
76      new HashMap<String, LongSampleIndex>();
77  
78    // These are bigger than m_doubleMap.size() and m_longMap.size()
79    // as the sample indicies also use slots.
80    private final int m_numberOfDoubles;
81    private final int m_numberOfLongs;
82  
83    /**
84     * Special slot for the HTTP plugin so it doesn't steal "user"
85     * indicies. Use with {@link #getLongIndex(String)}.
86     */
87    public static final String HTTP_PLUGIN_RESPONSE_STATUS_KEY =
88      "httpplugin.responseStatus";
89  
90    /**
91     * Special slot for the HTTP plugin so it doesn't steal "user"
92     * indicies. Use with {@link #getLongIndex(String)}.
93     */
94    public static final String HTTP_PLUGIN_RESPONSE_LENGTH_KEY =
95      "httpplugin.responseLength";
96  
97    /**
98     * Special slot for the HTTP plugin so it doesn't steal "user"
99     * indices. Use with {@link #getLongIndex(String)}.
100    */
101   public static final String HTTP_PLUGIN_RESPONSE_ERRORS_KEY =
102     "httpplugin.responseErrors";
103 
104   /**
105    * Special slot for the HTTP plugin so it doesn't steal "user"
106    * indices. Use with {@link #getLongIndex(String)}.
107    */
108   public static final String HTTP_PLUGIN_DNS_TIME_KEY =
109     "httpplugin.dnsTime";
110 
111   /**
112    * Special slot for the HTTP plugin so it doesn't steal "user"
113    * indices. Use with {@link #getLongIndex(String)}.
114    */
115   public static final String HTTP_PLUGIN_CONNECT_TIME_KEY =
116     "httpplugin.connectTime";
117 
118   /**
119    * Special slot for the HTTP plugin so it doesn't steal "user"
120    * indices. Use with {@link #getLongIndex(String)}.
121    */
122   public static final String HTTP_PLUGIN_FIRST_BYTE_TIME_KEY =
123     "httpplugin.firstByteTime";
124 
125   /**
126    * Special slot for the HTTP plugin so it doesn't steal "user"
127    * indices. Use with {@link #getLongIndex(String)}.
128    */
129   public static final String HTTP_PLUGIN_CONNECTIONS_ESTABLISHED =
130     "httpplugin.connectionsEstablished";
131 
132   /**
133    * Constructor.
134    */
135   StatisticsIndexMap() {
136     // Set up standard statistic index values. When adding new values
137     // or changing the order, you should also change the serialVersionUID
138     // of TestStatisticsMap.
139     this(asList("errors",
140                 "untimedTests",
141                 HTTP_PLUGIN_RESPONSE_STATUS_KEY,
142                 HTTP_PLUGIN_RESPONSE_LENGTH_KEY,
143                 HTTP_PLUGIN_RESPONSE_ERRORS_KEY,
144                 HTTP_PLUGIN_DNS_TIME_KEY,
145                 HTTP_PLUGIN_CONNECT_TIME_KEY,
146                 HTTP_PLUGIN_FIRST_BYTE_TIME_KEY,
147                 HTTP_PLUGIN_CONNECTIONS_ESTABLISHED,
148                 "userLong0",
149                 "userLong1",
150                 "userLong2",
151                 "userLong3",
152                 "userLong4"),
153          asList("peakTPS",
154                 "userDouble0",
155                 "userDouble1",
156                 "userDouble2",
157                 "userDouble3",
158                 "userDouble4"),
159          asList("period"),
160          asList("timedTests"));
161   }
162 
163   /**
164    * Open constructor for use by unit tests.
165    *
166    * @param longNames
167    *          Names of long statistics.
168    * @param doubleNames
169    *          Names of double statistics.
170    * @param transientLongNames
171    *          Names of transient long statistics.
172    * @param longSampleNames
173    *          Names of long sample statistics.
174    */
175   StatisticsIndexMap(List<String> longNames,
176                      List<String> doubleNames,
177                      List<String> transientLongNames,
178                      List<String> longSampleNames) {
179     int nextLongIndex = 0;
180     int nextTransientLongIndex = 0;
181 
182     for (String longName : longNames) {
183       m_longMap.put(longName, new LongIndex(nextLongIndex++));
184     }
185 
186     int nextDoubleIndex = 0;
187 
188     for (String doubleName : doubleNames) {
189       m_doubleMap.put(doubleName, new DoubleIndex(nextDoubleIndex++));
190     }
191 
192     for (String longSampleName : longSampleNames) {
193       createLongSampleIndex(longSampleName,
194                             new LongIndex(nextLongIndex++),
195                             new LongIndex(nextLongIndex++),
196                             new DoubleIndex(nextDoubleIndex++));
197     }
198 
199     for (String transientLongName : transientLongNames) {
200       m_transientLongMap.put(transientLongName,
201                              new LongIndex(nextTransientLongIndex++, true));
202     }
203 
204     m_numberOfDoubles = nextDoubleIndex;
205     m_numberOfLongs = nextLongIndex;
206   }
207 
208   int getNumberOfDoubles() {
209     return m_numberOfDoubles;
210   }
211 
212   int getNumberOfLongs() {
213     return m_numberOfLongs;
214   }
215 
216   int getNumberOfTransientLongs() {
217     return m_transientLongMap.size();
218   }
219 
220   Collection<DoubleSampleIndex> getDoubleSampleIndicies() {
221     return m_doubleSampleMap.values();
222   }
223 
224   Collection<LongSampleIndex> getLongSampleIndicies() {
225     return m_longSampleMap.values();
226   }
227 
228   /**
229    * Obtain the index object for the named double statistic.
230    *
231    * @param statisticName The statistic name.
232    * @return The index object, or <code>null</code> if there is no such
233    * double statistic.
234    */
235   public DoubleIndex getDoubleIndex(String statisticName) {
236     return m_doubleMap.get(statisticName);
237   }
238 
239   /**
240    * Obtain the index object for the named long statistic.
241    *
242    * @param statisticName The statistic name.
243    * @return The index object, or <code>null</code> if there is no such
244    * long statistic.
245    */
246   public LongIndex getLongIndex(String statisticName) {
247     final LongIndex nonTransient = m_longMap.get(statisticName);
248 
249     if (nonTransient != null) {
250       return nonTransient;
251     }
252     else {
253       return m_transientLongMap.get(statisticName);
254     }
255   }
256 
257   /**
258    * Obtain the index object for the named double sample statistic.
259    *
260    * @param statisticName The statistic name.
261    * @return The index object, or <code>null</code> if there is no such
262    * double sample statistic.
263    */
264   public DoubleSampleIndex getDoubleSampleIndex(String statisticName) {
265     return m_doubleSampleMap.get(statisticName);
266   }
267 
268   /**
269    * Obtain the index object for the named long statistic.
270    *
271    * @param statisticName The statistic name.
272    * @return The index object, or <code>null</code> if there is no such
273    * long sample statistic.
274    */
275   public LongSampleIndex getLongSampleIndex(String statisticName) {
276     return m_longSampleMap.get(statisticName);
277   }
278 
279   /**
280    * Factory for {@link LongSampleIndex}s.
281    *
282    * <p>If this is made public and called from somewhere other than our
283    * constructor, we need to address synchronisation.</p>
284    *
285    * @param statisticName Name to register index under.
286    * @param sumIndex Index to hold sum.
287    * @param countIndex Index to hold count.
288    * @param varianceIndex Index to hold variance.
289    * @return The new index.
290    */
291   private LongSampleIndex createLongSampleIndex(String statisticName,
292                                                 LongIndex sumIndex,
293                                                 LongIndex countIndex,
294                                                 DoubleIndex varianceIndex) {
295     final LongSampleIndex result =
296       new LongSampleIndex(sumIndex, countIndex, varianceIndex);
297 
298     m_longSampleMap.put(statisticName, result);
299 
300     return result;
301   }
302 
303   /**
304    * Factory for {@link DoubleSampleIndex}s.
305    *
306    * <p>Package scope for unit tests.</p>
307    *
308    * <p>If this is made public and called from somewhere other than our
309    * constructor, we need to address synchronisation.</p>
310    *
311    * @param statisticName Name to register index under.
312    * @param sumIndex Index to hold sum.
313    * @param countIndex Index to hold count.
314    * @param varianceIndex Index to hold variance.
315    * @return The new index.
316    */
317   DoubleSampleIndex createDoubleSampleIndex(String statisticName,
318                                             DoubleIndex sumIndex,
319                                             LongIndex countIndex,
320                                             DoubleIndex varianceIndex) {
321     final DoubleSampleIndex result =
322       new DoubleSampleIndex(sumIndex, countIndex, varianceIndex);
323 
324     m_doubleSampleMap.put(statisticName, result);
325 
326     return result;
327   }
328 
329   /**
330    * For unit tests to remove {@link DoubleSampleIndex}s they've created.
331    *
332    * @param statisticName Name of the DoubleSampleIndex.
333    */
334   void removeDoubleSampleIndex(String statisticName) {
335     m_doubleSampleMap.remove(statisticName);
336   }
337 
338   /**
339    * Base for index classes.
340    *
341    * <p>
342    * Refactor me: move the index implementations to nested classes of
343    * StatisticsSetImplementation and expose a single, opaque StatsiticsSet.Index
344    * interface.
345    * </p>
346    */
347   abstract static class AbstractSimpleIndex {
348 
349     private final int m_value;
350     private final boolean m_transient;
351 
352     protected AbstractSimpleIndex(int i, boolean isTransient) {
353       m_value = i;
354       m_transient = isTransient;
355     }
356 
357     public final int getValue() {
358       return m_value;
359     }
360 
361     public boolean isTransient() {
362       return m_transient;
363     }
364   }
365 
366   /**
367    * Class of opaque objects that represent <code>double</code> statistics.
368    */
369   public static final class DoubleIndex extends AbstractSimpleIndex {
370     private DoubleIndex(int i) {
371       super(i, false);
372     }
373   }
374 
375   /**
376    * Class of opaque objects that represent <code>long</code> statistics.
377    */
378   public static final class LongIndex extends AbstractSimpleIndex {
379     private LongIndex(int i) {
380       this(i, false);
381     }
382 
383     private LongIndex(int i, boolean isTransient) {
384       super(i, isTransient);
385     }
386   }
387 
388   /**
389    * Base class for sample statistic indices.
390    */
391   static class SampleIndex {
392     private final LongIndex m_countIndex;
393     private final DoubleIndex m_varianceIndex;
394 
395     protected SampleIndex(LongIndex countIndex, DoubleIndex varianceIndex) {
396       m_countIndex = countIndex;
397       m_varianceIndex = varianceIndex;
398     }
399 
400     /**
401      * Get the index object for our count (the number of samples).
402      *
403      * <p>Package scope to prevent direct write access. External clients should
404      * use the {@link StatisticsSet} or {@link StatisticExpression} interfaces.
405      * </p>
406      *
407      * @return The index object.
408      */
409     final LongIndex getCountIndex() {
410       return m_countIndex;
411     }
412 
413     /**
414      * Get the index object for our variance.
415      *
416      * <p>Package scope to prevent direct write access. External clients should
417      * use the {@link StatisticsSet} or {@link StatisticExpression} interfaces.
418      * </p>
419      *
420      * @return The index object.
421      */
422     final DoubleIndex getVarianceIndex() {
423       return m_varianceIndex;
424     }
425   }
426 
427   /**
428    * Class of objects that represent sample statistics with <code>double</code>
429    * sample values.
430    */
431   public static final class DoubleSampleIndex extends SampleIndex {
432     private final DoubleIndex m_sumIndex;
433 
434     private DoubleSampleIndex(DoubleIndex sumIndex,
435                               LongIndex countIndex,
436                               DoubleIndex varianceIndex) {
437       super(countIndex, varianceIndex);
438       m_sumIndex = sumIndex;
439     }
440 
441     /**
442      * Get the index object for our sum.
443      *
444      * <p>Package scope to prevent direct write access. External clients should
445      * use the {@link StatisticsSet} or {@link StatisticExpression} interfaces.
446      * </p>
447      *
448      * @return The index object.
449      */
450     DoubleIndex getSumIndex() {
451       return m_sumIndex;
452     }
453   }
454 
455   /**
456    * Class of objects that represent sample statistics with <code>long</code>
457    * sample values.
458    */
459   public static final class LongSampleIndex extends SampleIndex {
460     private final LongIndex m_sumIndex;
461 
462     private LongSampleIndex(LongIndex sumIndex,
463                             LongIndex countIndex,
464                             DoubleIndex varianceIndex) {
465       super(countIndex, varianceIndex);
466       m_sumIndex = sumIndex;
467     }
468 
469     /**
470      * Get the index object for our sum.
471      *
472      * <p>Package scope to prevent direct write access. External clients should
473      * use the {@link StatisticsSet} or {@link StatisticExpression} interfaces.
474      * </p>
475      *
476      * @return The index object.
477      */
478     LongIndex getSumIndex() {
479       return m_sumIndex;
480     }
481   }
482 }