1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package net.grinder.console.model;
24
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.Timer;
32 import java.util.TimerTask;
33 import java.util.TreeSet;
34
35 import net.grinder.common.GrinderException;
36 import net.grinder.common.Test;
37 import net.grinder.console.common.ErrorHandler;
38 import net.grinder.console.common.Resources;
39 import net.grinder.statistics.PeakStatisticExpression;
40 import net.grinder.statistics.StatisticExpression;
41 import net.grinder.statistics.StatisticExpressionFactory;
42 import net.grinder.statistics.StatisticsIndexMap;
43 import net.grinder.statistics.StatisticsServices;
44 import net.grinder.statistics.StatisticsSet;
45 import net.grinder.statistics.TestStatisticsMap;
46 import net.grinder.util.ListenerSupport;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public final class SampleModelImplementation implements SampleModel {
62
63 private final ConsoleProperties m_properties;
64 private final StatisticsServices m_statisticsServices;
65 private final Timer m_timer;
66 private final ErrorHandler m_errorHandler;
67
68 private final String m_stateIgnoringString;
69 private final String m_stateWaitingString;
70 private final String m_stateStoppedString;
71 private final String m_stateCapturingString;
72 private final String m_unknownTestString;
73
74
75
76
77
78 private final Set<Test> m_tests = new TreeSet<Test>();
79
80 private final ListenerSupport<Listener> m_listeners =
81 new ListenerSupport<Listener>();
82
83 private final StatisticsIndexMap.LongIndex m_periodIndex;
84 private final StatisticExpression m_tpsExpression;
85 private final PeakStatisticExpression m_peakTPSExpression;
86
87 private final SampleAccumulator m_totalSampleAccumulator;
88
89
90
91
92 private final Map<Test, SampleAccumulator> m_accumulators =
93 Collections.synchronizedMap(new HashMap<Test, SampleAccumulator>());
94
95
96 private InternalState m_state;
97
98
99
100
101
102
103
104
105
106
107
108
109 public SampleModelImplementation(final ConsoleProperties properties,
110 final StatisticsServices statisticsServices,
111 final Timer timer,
112 final Resources resources,
113 final ErrorHandler errorHandler)
114 throws GrinderException {
115
116 m_properties = properties;
117 m_statisticsServices = statisticsServices;
118 m_timer = timer;
119 m_errorHandler = errorHandler;
120
121 m_stateIgnoringString = resources.getString("state.ignoring.label") + ' ';
122 m_stateWaitingString = resources.getString("state.waiting.label");
123 m_stateStoppedString = resources.getString("state.stopped.label");
124 m_stateCapturingString = resources.getString("state.capturing.label") + ' ';
125 m_unknownTestString = resources.getString("ignoringUnknownTest.text");
126
127 final StatisticsIndexMap indexMap =
128 statisticsServices.getStatisticsIndexMap();
129
130 m_periodIndex = indexMap.getLongIndex("period");
131
132 final StatisticExpressionFactory statisticExpressionFactory =
133 m_statisticsServices.getStatisticExpressionFactory();
134
135 m_tpsExpression = statisticsServices.getTPSExpression();
136
137 m_peakTPSExpression =
138 statisticExpressionFactory.createPeak(
139 indexMap.getDoubleIndex("peakTPS"), m_tpsExpression);
140
141 m_totalSampleAccumulator =
142 new SampleAccumulator(m_peakTPSExpression, m_periodIndex,
143 m_statisticsServices.getStatisticsSetFactory());
144
145 setInternalState(new WaitingForTriggerState());
146 }
147
148
149
150
151 @Override
152 public StatisticExpression getTPSExpression() {
153 return m_tpsExpression;
154 }
155
156
157
158
159 @Override
160 public StatisticExpression getPeakTPSExpression() {
161 return m_peakTPSExpression;
162 }
163
164
165
166
167 @Override
168 public void registerTests(final Collection<Test> tests) {
169
170 final Set<Test> newTests = new HashSet<Test>(tests);
171
172 final Test[] testArray;
173
174 synchronized (m_tests) {
175 newTests.removeAll(m_tests);
176
177 if (newTests.size() == 0) {
178
179 return;
180 }
181
182 m_tests.addAll(newTests);
183
184
185 testArray = m_tests.toArray(new Test[m_tests.size()]);
186 }
187
188 final SampleAccumulator[] accumulatorArray =
189 new SampleAccumulator[testArray.length];
190
191 synchronized (m_accumulators) {
192 for (final Test test : newTests) {
193 m_accumulators.put(test,
194 new SampleAccumulator(
195 m_peakTPSExpression,
196 m_periodIndex,
197 m_statisticsServices.getStatisticsSetFactory()));
198 }
199
200 for (int i = 0; i < accumulatorArray.length; i++) {
201 accumulatorArray[i] = m_accumulators.get(testArray[i]);
202 }
203 }
204
205 final ModelTestIndex modelTestIndex =
206 new ModelTestIndex(testArray, accumulatorArray);
207
208 m_listeners.apply(
209 new ListenerSupport.Informer<Listener>() {
210 @Override
211 public void inform(final Listener l) {
212 l.newTests(newTests, modelTestIndex); }
213 });
214 }
215
216
217
218
219 @Override
220 public StatisticsSet getTotalCumulativeStatistics() {
221 return m_totalSampleAccumulator.getCumulativeStatistics();
222 }
223
224
225
226
227 @Override
228 public StatisticsSet getTotalLatestStatistics() {
229 return m_totalSampleAccumulator.getLastSampleStatistics();
230 }
231
232
233
234
235 @Override
236 public void addModelListener(final Listener listener) {
237 m_listeners.add(listener);
238 }
239
240
241
242
243 @Override
244 public void addSampleListener(final Test test,
245 final SampleListener listener) {
246 final SampleAccumulator sampleAccumulator = m_accumulators.get(test);
247
248 if (sampleAccumulator != null) {
249 sampleAccumulator.addSampleListener(listener);
250 }
251 }
252
253
254
255
256 @Override
257 public void addTotalSampleListener(final SampleListener listener) {
258 m_totalSampleAccumulator.addSampleListener(listener);
259 }
260
261
262
263
264 @Override
265 public void reset() {
266
267 synchronized (m_tests) {
268 m_tests.clear();
269 }
270
271 m_accumulators.clear();
272 m_totalSampleAccumulator.zero();
273
274 m_listeners.apply(
275 new ListenerSupport.Informer<Listener>() {
276 @Override
277 public void inform(final Listener l) { l.resetTests(); }
278 });
279 }
280
281
282
283
284 @Override
285 public void zeroStatistics() {
286 zero();
287 }
288
289
290
291
292 @Override
293 public void start() {
294 getInternalState().start();
295 }
296
297
298
299
300 @Override
301 public void stop() {
302 getInternalState().stop();
303 }
304
305
306
307
308 @Override
309 public void addTestReport(final TestStatisticsMap testStatisticsMap) {
310 getInternalState().newTestReport(testStatisticsMap);
311 }
312
313
314
315
316 @Override
317 public State getState() {
318 return getInternalState().toExternalState();
319 }
320
321 private void zero() {
322 synchronized (m_accumulators) {
323 for (final SampleAccumulator sampleAccumulator :
324 m_accumulators.values()) {
325 sampleAccumulator.zero();
326 }
327 }
328
329 m_totalSampleAccumulator.zero();
330 }
331
332 private InternalState getInternalState() {
333 synchronized (this) {
334 return m_state;
335 }
336 }
337
338 private void setInternalState(final InternalState newState) {
339 synchronized (this) {
340 m_state = newState;
341 }
342
343 m_listeners.apply(
344 new ListenerSupport.Informer<Listener>() {
345 @Override
346 public void inform(final Listener l) { l.stateChanged(); }
347 });
348 }
349
350 private interface InternalState {
351 State toExternalState();
352
353 void start();
354
355 void stop();
356
357 void newTestReport(TestStatisticsMap testStatisticsMap);
358 }
359
360 private abstract class AbstractInternalState
361 implements InternalState, State {
362
363 protected final boolean isActiveState() {
364 return getInternalState() == this;
365 }
366
367 @Override
368 public final State toExternalState() {
369
370 return this;
371 }
372
373 @Override
374 public final void start() {
375
376 setInternalState(new WaitingForTriggerState());
377 }
378
379 @Override
380 public final void stop() {
381
382 setInternalState(new StoppedState());
383 }
384
385 @Override
386 public long getSampleCount() {
387 return -1;
388 }
389 }
390
391 private final class WaitingForTriggerState extends AbstractInternalState {
392 public WaitingForTriggerState() {
393 zero();
394 }
395
396 @Override
397 public void newTestReport(final TestStatisticsMap testStatisticsMap) {
398 if (m_properties.getIgnoreSampleCount() == 0) {
399 setInternalState(new CapturingState());
400 }
401 else {
402 setInternalState(new TriggeredState());
403 }
404
405
406 getInternalState().newTestReport(testStatisticsMap);
407 }
408
409 @Override
410 public String getDescription() {
411 return m_stateWaitingString;
412 }
413
414 @Override
415 public Value getValue() {
416 return Value.WaitingForFirstReport;
417 }
418 }
419
420 private final class StoppedState extends AbstractInternalState {
421 @Override
422 public void newTestReport(final TestStatisticsMap testStatisticsMap) {
423 }
424
425 @Override
426 public String getDescription() {
427 return m_stateStoppedString;
428 }
429
430 @Override
431 public Value getValue() {
432 return Value.Stopped;
433 }
434 }
435
436 private abstract class SamplingState extends AbstractInternalState {
437
438 private long m_lastTime = 0;
439
440 private volatile long m_sampleCount = 1;
441
442 @Override
443 public final void newTestReport(final TestStatisticsMap testStatisticsMap) {
444 testStatisticsMap.new ForEach() {
445 @Override
446 public void next(final Test test, final StatisticsSet statistics) {
447 final SampleAccumulator sampleAccumulator = m_accumulators.get(test);
448
449 if (sampleAccumulator == null) {
450 m_errorHandler.handleInformationMessage(
451 m_unknownTestString + " " + test);
452 }
453 else {
454 sampleAccumulator.addIntervalStatistics(statistics);
455
456 if (shouldAccumulateSamples()) {
457 sampleAccumulator.addCumulativeStaticstics(statistics);
458 }
459
460 if (!statistics.isComposite()) {
461 m_totalSampleAccumulator.addIntervalStatistics(statistics);
462
463 if (shouldAccumulateSamples()) {
464 m_totalSampleAccumulator.addCumulativeStaticstics(statistics);
465 }
466 }
467 }
468 }
469 }
470 .iterate();
471 }
472
473 protected final void schedule() {
474 synchronized (this) {
475 if (m_lastTime == 0) {
476 m_lastTime = System.currentTimeMillis();
477 }
478 }
479
480 m_timer.schedule(
481 new TimerTask() {
482 @Override
483 public void run() { sample(); }
484 },
485 m_properties.getSampleInterval());
486 }
487
488 public final void sample() {
489 if (!isActiveState()) {
490 return;
491 }
492
493 try {
494 final long period;
495
496 synchronized (this) {
497 period = System.currentTimeMillis() - m_lastTime;
498 }
499
500 final long sampleInterval = m_properties.getSampleInterval();
501
502 synchronized (m_accumulators) {
503 for (final SampleAccumulator sampleAccumulator :
504 m_accumulators.values()) {
505 sampleAccumulator.fireSample(sampleInterval, period);
506 }
507 }
508
509 m_totalSampleAccumulator.fireSample(sampleInterval, period);
510
511 ++m_sampleCount;
512
513
514
515
516
517 setInternalState(nextState());
518
519 m_listeners.apply(
520 new ListenerSupport.Informer<Listener>() {
521 @Override
522 public void inform(final Listener l) { l.newSample(); }
523 });
524 }
525 finally {
526 synchronized (this) {
527 if (isActiveState()) {
528 schedule();
529 }
530 }
531 }
532 }
533
534 @Override
535 public final long getSampleCount() {
536 return m_sampleCount;
537 }
538
539 protected abstract boolean shouldAccumulateSamples();
540
541 protected abstract InternalState nextState();
542 }
543
544 private final class TriggeredState extends SamplingState {
545 public TriggeredState() {
546 schedule();
547 }
548
549 @Override
550 protected boolean shouldAccumulateSamples() {
551 return false;
552 }
553
554 @Override
555 protected InternalState nextState() {
556 if (getSampleCount() > m_properties.getIgnoreSampleCount()) {
557 return new CapturingState();
558 }
559
560 return this;
561 }
562
563 @Override
564 public String getDescription() {
565 return m_stateIgnoringString + getSampleCount();
566 }
567
568 @Override
569 public Value getValue() {
570 return Value.IgnoringInitialSamples;
571 }
572 }
573
574 private final class CapturingState extends SamplingState {
575 public CapturingState() {
576 zero();
577 schedule();
578 }
579
580 @Override
581 protected boolean shouldAccumulateSamples() {
582 return true;
583 }
584
585 @Override
586 protected InternalState nextState() {
587 final int collectSampleCount = m_properties.getCollectSampleCount();
588
589 if (collectSampleCount != 0 && getSampleCount() > collectSampleCount) {
590 return new StoppedState();
591 }
592
593 return this;
594 }
595
596 @Override
597 public String getDescription() {
598 return m_stateCapturingString + getSampleCount();
599 }
600
601 @Override
602 public Value getValue() {
603 return Value.Recording;
604 }
605 }
606 }