EMMA Coverage Report (generated Sun Aug 17 11:20:34 PDT 2014)
[all classes][net.digitaltsunami.tmeter]

COVERAGE SUMMARY FOR SOURCE FILE [TimeTracker.java]

nameclass, %method, %block, %line, %
TimeTracker.java100% (3/3)88%  (28/32)91%  (243/266)88%  (78.8/90)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class TimeTracker$ActionChainSingleton100% (1/1)67%  (2/3)70%  (7/10)67%  (2/3)
TimeTracker$ActionChainSingleton (): void 0%   (0/1)0%   (0/3)0%   (0/1)
<static initializer> 100% (1/1)100% (5/5)100% (1/1)
getInstance (): ActionChain 100% (1/1)100% (2/2)100% (1/1)
     
class TimeTracker100% (1/1)89%  (24/27)92%  (219/239)87%  (69.8/80)
TimeTracker (): void 0%   (0/1)0%   (0/3)0%   (0/2)
clearActionChain (): void 0%   (0/1)0%   (0/3)0%   (0/2)
shutdown (boolean): void 0%   (0/1)0%   (0/9)0%   (0/5)
startRecording (TimerLevel, String): Timer 100% (1/1)92%  (61/66)94%  (18.8/20)
<static initializer> 100% (1/1)100% (29/29)100% (7/7)
access$000 (String): void 100% (1/1)100% (3/3)100% (1/1)
access$100 (): boolean 100% (1/1)100% (2/2)100% (1/1)
addCompletionAction (TimerAction): void 100% (1/1)100% (7/7)100% (3/3)
clear (): void 100% (1/1)100% (7/7)100% (4/4)
clearTimerLevels (): void 100% (1/1)100% (3/3)100% (2/2)
decrementConcurrent (String): void 100% (1/1)100% (11/11)100% (4/4)
disableTimerLevel (TimerLevel): boolean 100% (1/1)100% (4/4)100% (1/1)
disableTimerLevels (TimerLevel []): boolean 100% (1/1)100% (26/26)100% (4/4)
enableTimerLevel (TimerLevel): TimerLevel 100% (1/1)100% (4/4)100% (1/1)
enableTimerLevels (TimerLevel []): void 100% (1/1)100% (5/5)100% (2/2)
getActionChain (): ActionChain 100% (1/1)100% (2/2)100% (1/1)
getCurrentTimers (): Timer [] 100% (1/1)100% (6/6)100% (1/1)
getDefaultTimeRecorder (): TimeRecorder 100% (1/1)100% (2/2)100% (1/1)
isKeepList (): boolean 100% (1/1)100% (2/2)100% (1/1)
isTrackConcurrent (): boolean 100% (1/1)100% (2/2)100% (1/1)
isTrackingDisabled (): boolean 100% (1/1)100% (2/2)100% (1/1)
setActionChain (ActionChain): void 100% (1/1)100% (21/21)100% (6/6)
setDefaultTimeRecorder (TimeRecorder): void 100% (1/1)100% (3/3)100% (2/2)
setKeepList (boolean): void 100% (1/1)100% (3/3)100% (2/2)
setTrackConcurrent (boolean): void 100% (1/1)100% (7/7)100% (3/3)
setTrackingDisabled (boolean): void 100% (1/1)100% (3/3)100% (2/2)
startRecording (String): Timer 100% (1/1)100% (4/4)100% (1/1)
     
class TimeTracker$TimerStoppedEventHandler100% (1/1)100% (2/2)100% (17/17)100% (7/7)
TimeTracker$TimerStoppedEventHandler (): void 100% (1/1)100% (3/3)100% (1/1)
timerStopped (TimerStoppedEvent): void 100% (1/1)100% (14/14)100% (6/6)

1/* __copyright_begin__
2   Copyright 2011 Dan Hagberg
3 
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7 
8       http://www.apache.org/licenses/LICENSE-2.0
9 
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15__copyright_end__ */
16package net.digitaltsunami.tmeter;
17 
18import java.util.ArrayList;
19import java.util.Collections;
20import java.util.List;
21import java.util.concurrent.ConcurrentHashMap;
22import java.util.concurrent.atomic.AtomicInteger;
23 
24import net.digitaltsunami.tmeter.action.ActionChain;
25import net.digitaltsunami.tmeter.action.TimerAction;
26import net.digitaltsunami.tmeter.event.TimerStoppedEvent;
27import net.digitaltsunami.tmeter.event.TimerStoppedListener;
28import net.digitaltsunami.tmeter.level.TimerLevel;
29import net.digitaltsunami.tmeter.level.TimerLevelCollection;
30import net.digitaltsunami.tmeter.level.TimerLevelSet;
31import net.digitaltsunami.tmeter.record.NullTimeRecorder;
32import net.digitaltsunami.tmeter.record.TimeRecorder;
33 
34/**
35 * A framework for recording elapsed time to perform a given task and to drive
36 * processing of the completed timers.
37 * <p>
38 * Provides options to allow the recording process to be configured for low to
39 * high volume recording.
40 * <p>
41 * List - Can be configured to keep a list of all timers started via this class.
42 * This can be used when logging would cause too much overhead such as high
43 * volume recordings.
44 * <ul>
45 * <li>A snapshot of the results can be returned during the run.
46 * <li>The results may be cleared during the run. If the testing is done in
47 * batches, then the results can be processed and the list cleared between
48 * batches. This will reduce the memory requirements of the timer framework
49 * </ul>
50 * <p>
51 * Logging - Logging can be enabled to direct the timers to log on completion.
52 * Logging can be directed to the console or a file. Current logging styles are
53 * text and csv. See {@link Timer#toString()} and {@link Timer#toCsv()} for
54 * formats.
55 * <p>
56 * Action chain - Post processing of completed timers can be taken out of the
57 * processing thread by use of an {@link ActionChain}. The Action Chain provides
58 * a queue for processing a user provided chain of {@link TimerAction}s that
59 * enable more complex processing of the timer data. See {@link ActionChain} for
60 * more information.
61 * <p>
62 * Disabling - Allows the creation of timers to be enabled/disabled during
63 * processing so that the timer logic can be left in place with very little
64 * overhead when disabled. When disabled, a single instance of
65 * {@link TimerShell} will be returned for all timer recording requests.
66 * <p>
67 * Concurrent counts - Can maintain concurrent task counts if enabled. This
68 * provides a rudimentary concurrent count for all timers recording the same
69 * task - as determined by task name. The count is based on the number of
70 * currently running tasks for a task name at the time the Timer was created.
71 * 
72 * 
73 * @author dhagberg
74 * 
75 */
76public class TimeTracker {
77    /**
78     * Indicates whether or not we are keeping a list of all timers.
79     */
80    private static boolean keepList;
81    /**
82     * Indicates whether or not we are keeping track of concurrent task count.
83     */
84    private static boolean trackConcurrent;
85 
86    /**
87     * Indicates how to record the timer when it is stopped. This setting will
88     * be used as a default to set the corresponding entry on each timer as it
89     * is created.
90     */
91    private static TimeRecorder defaultTimeRecorder = NullTimeRecorder.getInstance();
92 
93    /**
94     * List of all timers created since the keepList value was set to true.
95     */
96    private static final List<Timer> timerList =
97            Collections.synchronizedList(new ArrayList<Timer>());
98 
99    /**
100     * Current count of timers by task name.
101     */
102    private static final ConcurrentHashMap<String, AtomicInteger> concurrentMap =
103            new ConcurrentHashMap<String, AtomicInteger>();
104 
105    /**
106     * Singleton {@link Timer} used when time tracking is turned off.
107     */
108    private static final Timer dummy = new TimerShell("TimerShellTask");
109 
110    /**
111     * Indicates if tracking is disabled. If so, a {@link TimerShell} will be
112     * returned by {@link #startRecording(String)}.
113     */
114    private static boolean trackingDisabled;
115 
116    /**
117     * Listener for {@link TimerStoppedEvent}. Will drive any configured
118     * completion processing.
119     */
120    private static final TimerStoppedEventHandler completionEventListener =
121            new TimerStoppedEventHandler();
122 
123    /**
124     * Indicates whether or not to register for the completion event when
125     * creating new timers.
126     */
127    private static boolean listenForCompletion;
128 
129    /**
130     * Default {@link TimerLevel} used when creating {@link Timer} instances if
131     * one is not provided.
132     */
133    private static final TimerLevel DEFAULT_LEVEL = null;
134    /**
135     * Current set of timer levels that are active. If none provided, then all
136     * timer levels are active by default.
137     */
138    private static final TimerLevelCollection filter = new TimerLevelSet();
139 
140    /**
141     * No instances of TimeTracker
142     */
143    private TimeTracker() {
144        // No instances
145    }
146 
147    /**
148     * Create and configure a {@link Timer} instance as applicable. All
149     * configuration settings are done prior to starting the timer to reduce the
150     * effect of the overhead in the timing results.
151     * <p>
152     * If timing is disabled, an instance of {@link TimerShell} will be returned
153     * so that the timing code can be left in place.
154     * <p>
155     * If a timer level is provided, it will be checked against the current
156     * filter. If not enabled, then an instance of {@link TimerShell} will be
157     * returned.
158     * 
159     * @param taskName
160     *            Name used to represent a given task.
161     * @param level
162     *            {@link TimerLevel} of timer requested. Will be used to
163     *            determine if the timer is enabled for the request.
164     * @return instance of {@link Timer} configured based on current settings.
165     * @see #setTrackingDisabled(boolean)
166     */
167    public static Timer startRecording(TimerLevel level, String taskName) {
168        // If not currently tracking time, return a shell so that invoking code
169        // does not have to change.
170        if (trackingDisabled) {
171            return dummy;
172        }
173 
174        // If a level was not provided, then skip over filtering for this timer,
175        // otherwise ensure that the level active.
176        if (level != DEFAULT_LEVEL) {
177            if (!filter.isEnabled(level)) {
178                return dummy;
179            }
180        }
181 
182        Timer timer = new Timer(taskName, true, defaultTimeRecorder, level);
183        // Do all time intensive settings prior to starting time
184        // keeping list
185        if (keepList) {
186            timerList.add(timer);
187        }
188        // tracking concurrent
189        if (trackConcurrent) {
190            AtomicInteger concurrent = concurrentMap.get(taskName);
191            if (concurrent == null) {
192                // Task not found in list. Create a new counter
193                concurrent = new AtomicInteger();
194                // Place it in the map
195                AtomicInteger entry = concurrentMap.putIfAbsent(taskName,
196                        concurrent);
197                // If the same entry was not returned, another thread created
198                // during setup. Use the existing entry.
199                if (entry != null && entry != concurrent) {
200                    concurrent = entry;
201                }
202            }
203            timer.setConcurrent(concurrent.incrementAndGet());
204        }
205        if (listenForCompletion) {
206            timer.setCompletionListener(completionEventListener);
207        }
208        timer.start();
209        return timer;
210    }
211 
212    /**
213     * Create and configure a {@link Timer} instance as applicable. All
214     * configuration settings are done prior to starting the timer to reduce the
215     * effect of the overhead in the timing results.
216     * <p>
217     * If timing is disabled, an instance of {@link TimerShell} will be returned
218     * so that the timing code can be left in place.
219     * 
220     * @param taskName
221     *            Name used to represent a given task.
222     * @return instance of {@link Timer} configured based on current settings.
223     * @see #setTrackingDisabled(boolean)
224     */
225    public static Timer startRecording(String taskName) {
226        return startRecording(DEFAULT_LEVEL, taskName);
227    }
228 
229    /**
230     * Indicates whether or not we are keeping a list of all timers.
231     * 
232     * @return the keepList
233     */
234    public static boolean isKeepList() {
235        return keepList;
236    }
237 
238    /**
239     * Indicates whether or not we are keeping a list of all timers.
240     * 
241     * @param keepList
242     *            True to keep a list of timers, otherwise false.
243     */
244    public static void setKeepList(boolean keepList) {
245        TimeTracker.keepList = keepList;
246    }
247 
248    /**
249     * Indicates whether or not we are keeping track of concurrent task count.
250     * 
251     * @return the trackConcurrent
252     */
253    public static boolean isTrackConcurrent() {
254        return trackConcurrent;
255    }
256 
257    /**
258     * Indicates whether or not we are keeping track of concurrent task count.
259     * 
260     * @param trackConcurrent
261     *            True to keep track of concurrent task count, otherwise false.
262     */
263    public static void setTrackConcurrent(boolean trackConcurrent) {
264        TimeTracker.trackConcurrent = trackConcurrent;
265        // The decrement concurrent processing occurs in the listener, so it
266        // must be enabled if tracking is enabled. Leave on if already on
267        listenForCompletion = (listenForCompletion | trackConcurrent);
268    }
269 
270    /**
271     * Decrement the concurrent count for the provided task name.
272     * 
273     * @param taskName
274     */
275    private static void decrementConcurrent(String taskName) {
276        AtomicInteger current = concurrentMap.get(taskName);
277        if (current != null) {
278            current.decrementAndGet();
279        }
280    }
281 
282    /**
283     * @return the default {@link TimeRecorder} used to populate the
284     *         corresponding field when creating {@link Timer}s
285     */
286    public static TimeRecorder getDefaultTimeRecorder() {
287        return defaultTimeRecorder;
288    }
289 
290    /**
291     * Indicates the default method to record the timer when it is stopped. This
292     * setting will be used to set the corresponding entry on each timer as it
293     * is created. Changes to setting will affect only those timers created
294     * after this invocation.
295     * 
296     * @param logType
297     *            type of logging to occur on timer completion.
298     */
299    public static void setDefaultTimeRecorder(TimeRecorder defaultTimeRecorder) {
300        TimeTracker.defaultTimeRecorder = defaultTimeRecorder;
301    }
302 
303    /**
304     * Clears accumulated state. All configuration settings will remain intact.
305     * The values cleared depend on the settings, but may include:
306     * <ul>
307     * <li>List of timers</li>
308     * <li>Concurrent counts</li>
309     * <li>Data within {@link TimerAction} instances within the
310     * {@link ActionChain}. This will affect only the data and the action chain
311     * will remain intact.</li>
312     * </ul>
313     * <p>
314     */
315    public static void clear() {
316        timerList.clear();
317        concurrentMap.clear();
318        ActionChainSingleton.getInstance().reset();
319    }
320 
321    /**
322     * Indicates if tracking is disabled. If so, a {@link TimerShell} will be
323     * returned by {@link #startRecording(String)}.
324     * 
325     * @return
326     */
327    public static boolean isTrackingDisabled() {
328        return trackingDisabled;
329    }
330 
331    /**
332     * Set tracking to enabled/disabled. If disabled, a {@link TimerShell} will
333     * be returned by {@link #startRecording(String)}.
334     * <p>
335     * Default is enabled.
336     * <p>
337     * If tracking is disabled, then all {@link TimerLevel}s enabled for this
338     * session are disabled as well. If tracking is re-enabled, then the current
339     * set of enabled timer levels will be become active.
340     */
341    public static void setTrackingDisabled(boolean trackingDisabled) {
342        TimeTracker.trackingDisabled = trackingDisabled;
343    }
344 
345    /**
346     * Shutdown all time tracking related processing threads.
347     */
348    public static void shutdown(boolean waitForTermination) {
349        ActionChain ac = ActionChainSingleton.getInstance();
350        if (ac != null) {
351            ac.shutdown();
352        }
353        listenForCompletion = false;
354    }
355 
356    /**
357     * Return the current post completion action processor.
358     * 
359     * @return
360     */
361    public static ActionChain getActionChain() {
362        return ActionChainSingleton.getInstance();
363    }
364 
365    /**
366     * Set the current post completion action processor.
367     */
368    public static void setActionChain(ActionChain newActionChain) {
369        ActionChain actionChain = ActionChainSingleton.getInstance();
370        for (TimerAction action : newActionChain.getActions()) {
371            actionChain.addAction(action);
372        }
373 
374        // Activate the timer completion listener as this is where interaction
375        // with the action processor occurs.
376        listenForCompletion = true;
377    }
378 
379    /**
380     * Add an action to the chain of actions that will be performed upon each
381     * timer completion.
382     * 
383     * @param action
384     */
385    public static void addCompletionAction(TimerAction action) {
386        ActionChainSingleton.getInstance().addAction(action);
387        listenForCompletion = true;
388    }
389 
390    /**
391     * Returns a copy of the current list of {@link Timer} entries. The copy is
392     * a shallow copy; therefore, the instances may change after they are
393     * returned. See {@link Timer} for a list of invariants.
394     * 
395     * @return a snapshot of the current list of timers.
396     */
397    public static Timer[] getCurrentTimers() {
398        return timerList.toArray(new Timer[0]);
399    }
400 
401    /**
402     * Enable a {@link TimerLevel} for recording. All subsequent timer requests
403     * enabled for this level will start a timer recording.
404     * 
405     * @param level
406     *            to enable for recording.
407     * @return timer level currently in filter if matches new level or null if
408     *         no matching timer. See {@link TimerLevelCollection} for
409     *         information on possible return values.
410     * @see TimerLevelCollection
411     * @see #setTrackingDisabled(boolean)
412     */
413    public static TimerLevel enableTimerLevel(TimerLevel level) {
414        return filter.addLevel(level);
415    }
416 
417    /**
418     * Enable all provided {@link TimerLevel}s for recording. All subsequent
419     * timer requests enabled for these levels will start a timer recording.
420     * 
421     * @param levels
422     *            to enable for recording.
423     * @return
424     * @see TimerLevelCollection
425     * @see #setTrackingDisabled(boolean)
426     */
427    public static void enableTimerLevels(TimerLevel... levels) {
428        filter.addLevels(levels);
429    }
430 
431    /**
432     * Disable a {@link TimerLevel} for recording. Subsequent requests will not
433     * be enabled for this level and recording will not be started.
434     * 
435     * @param level
436     *            to disable timer recording.
437     * @return true if level matched an existing entry and was removed.
438     * @see TimerLevelCollection
439     * @see #setTrackingDisabled(boolean)
440     */
441    public static boolean disableTimerLevel(TimerLevel level) {
442        return filter.removeLevel(level);
443    }
444 
445    /**
446     * Disable provided {@link TimerLevel}s for recording. Subsequent requests
447     * will not be enabled for these levels and recording will not be started.
448     * 
449     * @param levels
450     *            to disable timer recording.
451     * @return true if any level matched an existing entry and was removed.
452     * @see TimerLevelCollection
453     * @see #setTrackingDisabled(boolean)
454     */
455    public static boolean disableTimerLevels(TimerLevel... levels) {
456        boolean disabled = false;
457        for (TimerLevel level : levels) {
458            disabled |= filter.removeLevel(level);
459        }
460        return disabled;
461    }
462 
463    /**
464     * Disable all {@link TimerLevel}s for recording. Subsequent requests will
465     * not be enabled for any levels and recording will not be started.
466     * <p>
467     * Note: This does non include timers with out a timer level as those are
468     * not affected by the filter.
469     * 
470     * @see TimerLevelCollection
471     * @see #setTrackingDisabled(boolean)
472     */
473    public static void clearTimerLevels() {
474        filter.clear();
475    }
476 
477    /**
478     * Listener for timers being stopped.
479     * 
480     * @author dhagberg
481     */
482    static class TimerStoppedEventHandler implements TimerStoppedListener {
483        @Override
484        public void timerStopped(TimerStoppedEvent event) {
485            Timer timer = event.getTimer();
486            if (TimeTracker.isTrackConcurrent()) {
487                TimeTracker.decrementConcurrent(timer.getTaskName());
488            }
489            if (listenForCompletion) {
490                ActionChainSingleton.getInstance().submitCompletedTimer(timer);
491            }
492        }
493    }
494 
495    /**
496     * Clear out the action chain. This will cause the action chain to complete
497     * processing and then terminate.
498     */
499    public static void clearActionChain() {
500        ActionChainSingleton.getInstance().clearActions();
501    }
502 
503    /**
504     * Singleton provider for lazy instantiation of ActionChain
505     */
506    private static class ActionChainSingleton {
507        private static ActionChain actionChainInstance = new ActionChain();
508 
509        static ActionChain getInstance() {
510            return actionChainInstance;
511        }
512    }
513}

[all classes][net.digitaltsunami.tmeter]
EMMA 2.1.5320 (stable) (C) Vladimir Roubtsov