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

COVERAGE SUMMARY FOR SOURCE FILE [Timer.java]

nameclass, %method, %block, %line, %
Timer.java100% (3/3)98%  (39/40)96%  (618/643)98%  (130.8/134)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Timer$1100% (1/1)100% (1/1)88%  (23/26)88%  (0.9/1)
<static initializer> 100% (1/1)88%  (23/26)88%  (0.9/1)
     
class Timer$TimerStatus100% (1/1)75%  (3/4)90%  (43/48)94%  (1.9/2)
valueOf (String): Timer$TimerStatus 0%   (0/1)0%   (0/5)0%   (0/1)
<static initializer> 100% (1/1)100% (34/34)100% (2/2)
Timer$TimerStatus (String, int): void 100% (1/1)100% (5/5)100% (1/1)
values (): Timer$TimerStatus [] 100% (1/1)100% (4/4)100% (1/1)
     
class Timer100% (1/1)100% (35/35)97%  (552/569)98%  (129/132)
stop (Boolean, Object []): long 100% (1/1)77%  (37/48)78%  (7/9)
fromCsv (String): Timer 100% (1/1)89%  (47/53)90%  (9/10)
Timer (String): void 100% (1/1)100% (7/7)100% (2/2)
Timer (String, String): void 100% (1/1)100% (6/6)100% (2/2)
Timer (String, String, TimerLevel): void 100% (1/1)100% (12/12)100% (5/5)
Timer (String, TimerLevel): void 100% (1/1)100% (7/7)100% (2/2)
Timer (String, boolean, TimeRecorder): void 100% (1/1)100% (7/7)100% (2/2)
Timer (String, boolean, TimeRecorder, TimerLevel): void 100% (1/1)100% (23/23)100% (9/9)
Timer (String, boolean, TimerLevel): void 100% (1/1)100% (7/7)100% (2/2)
getConcurrent (): int 100% (1/1)100% (3/3)100% (1/1)
getCsvHeader (): String 100% (1/1)100% (48/48)100% (9/9)
getElapsedMillis (): long 100% (1/1)100% (23/23)100% (5/5)
getElapsedNanos (): long 100% (1/1)100% (4/4)100% (1/1)
getElapsedNanos (boolean): long 100% (1/1)100% (26/26)100% (4/4)
getNotes (): TimerNotes 100% (1/1)100% (3/3)100% (1/1)
getStartTimeMillis (): long 100% (1/1)100% (12/12)100% (3/3)
getStartTimeNanos (): long 100% (1/1)100% (12/12)100% (3/3)
getStatus (): Timer$TimerStatus 100% (1/1)100% (3/3)100% (1/1)
getStopTimeNanos (): long 100% (1/1)100% (12/12)100% (3/3)
getTaskName (): String 100% (1/1)100% (3/3)100% (1/1)
getThreadName (): String 100% (1/1)100% (3/3)100% (1/1)
getTimerLevel (): TimerLevel 100% (1/1)100% (3/3)100% (1/1)
isRunning (): boolean 100% (1/1)100% (8/8)100% (1/1)
isStopped (): boolean 100% (1/1)100% (8/8)100% (1/1)
setCompletionListener (TimerStoppedListener): void 100% (1/1)100% (4/4)100% (2/2)
setConcurrent (int): void 100% (1/1)100% (4/4)100% (2/2)
setKeyedNotes (Object []): void 100% (1/1)100% (7/7)100% (2/2)
setNotes (Object []): void 100% (1/1)100% (7/7)100% (2/2)
setTimeRecorder (TimeRecorder): void 100% (1/1)100% (4/4)100% (2/2)
start (): void 100% (1/1)100% (14/14)100% (5/5)
stop (): long 100% (1/1)100% (33/33)100% (8/8)
stop (Object []): long 100% (1/1)100% (6/6)100% (1/1)
toCsv (): String 100% (1/1)100% (61/61)100% (11/11)
toString (): String 100% (1/1)100% (68/68)100% (12/12)
updateStatus (Timer$TimerStatus): Timer$TimerStatus 100% (1/1)100% (20/20)100% (6/6)

1/* __copyright_begin__
2 
3   Copyright 2011 Dan Hagberg
4 
5   Licensed under the Apache License, Version 2.0 (the "License");
6   you may not use this file except in compliance with the License.
7   You may obtain a copy of the License at
8 
9       http://www.apache.org/licenses/LICENSE-2.0
10 
11   Unless required by applicable law or agreed to in writing, software
12   distributed under the License is distributed on an "AS IS" BASIS,
13   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   See the License for the specific language governing permissions and
15   limitations under the License.
16__copyright_end__ */
17package net.digitaltsunami.tmeter;
18 
19import java.io.Serializable;
20import java.util.Date;
21 
22import net.digitaltsunami.tmeter.action.ActionChain;
23import net.digitaltsunami.tmeter.event.TimerStoppedEvent;
24import net.digitaltsunami.tmeter.event.TimerStoppedListener;
25import net.digitaltsunami.tmeter.level.TimerLevel;
26import net.digitaltsunami.tmeter.record.QueuedTimeRecorder;
27import net.digitaltsunami.tmeter.record.TimeRecorder;
28 
29/**
30 * A record of elapsed time. Timers can be used independently or as part of the
31 * framework driven using {@link TimeTracker}.
32 * <p>
33 * Provides:
34 * <ul>
35 * <li>Immediate or delayed start to allow preparation tasks to be excluded in
36 * the timing.
37 * <li>Nanosecond precision recording of tasks.
38 * <li>Basic completion logging in text or CSV format. Default is to not log.
39 * <li>Configurable log location with stdout being the default.
40 * <li>Status
41 * <li>Domain specific notes. A list of notes can be added to the timer that may
42 * provide information useful to analyzing the results at a later time. These
43 * notes will be appended to both the Text and CSV outputs if used.
44 * </ul>
45 * 
46 * <strong>Thread Safety</strong>
47 * <p>
48 * To avoid synchronization, which could cause context swaps, instances of Timer
49 * are <strong>not</strong> thread safe. Some aspects are immutable and some
50 * will not change after they are set.
51 * <p>
52 * Immutable:
53 * <ul>
54 * <li>Task name</li>
55 * <li>Thread name</li>
56 * </ul>
57 * <p>
58 * Will not change once set:
59 * <ul>
60 * <li>Start Time</li>
61 * <li>Stop Time</li>
62 * <li>Elapsed Time - Can get a running time for elapsed time, but once the
63 * timer is stopped, this will not change.</li>
64 * <li>Status - Once stopped, cannot be set to any previous state.</li>
65 * </ul>
66 * 
67 * <strong>Usage:</strong>
68 * 
69 * <p>
70 * Simple - create and return a running Timer with no logging and task name of
71 * "BuildResults".
72 * 
73 * <pre>
74 * <code>
75 *     Timer brTimer = new Timer("BuildResults");
76 *     .
77 *     taskProcessing
78 *     .
79 *     brTimer.stop();
80 *     System.out.println("BuildResults: " + brTimer.getElapsedMillis());
81 * </code>
82 * Output: BuildResults: 1000
83 * 
84 * </pre>
85 * 
86 * More complex - create and return a non-running Timer with CSV logging to the
87 * console and a set of domain specific notes. The CSV line along with the notes
88 * will be written to stdout.
89 * 
90 * <pre>
91 * <code>
92 *     Timer serviceTimer = new Timer("BuildResults", true, new ConsoleTimeRecorder(TimerLogType.CSV));
93 *     serviceTimer.setNotes(siteName, 4, user);
94 *     serviceTimer.start();
95 *     .
96 *     taskProcessing
97 *     .
98 *     serviceTimer.stop();
99 * </code>
100 * Output: 1303060069651,BuildResults,main,1000,1000131000,0,TravelSite,4,member
101 * 
102 * </pre>
103 * 
104 * 
105 * @NotThreadSafe
106 * 
107 * @author dhagberg
108 * 
109 */
110public class Timer implements Serializable {
111    
112    private static final long serialVersionUID = -7476041689241631462L;
113    /**
114     * The name of the task for which the time is being recorded.
115     */
116    private final String taskName;
117    /**
118     * Wall clock start time for task. Time is recorded in milliseconds
119     * (1.0E-3).
120     * 
121     * @see {@link System#currentTimeMillis()}
122     */
123    private long startTimeMillis;
124    /**
125     * Start time for task in nanoseconds (1.0E-9). Does <strong>not</strong>
126     * represent wall clock time, but is used to measure elapsed time. Provides
127     * nanosecond precision, but not accuracy.
128     * 
129     * @see {@link System#nanoTime()}
130     */
131    private long startTimeNanos;
132    /**
133     * Stop time for task in nanoseconds (1.0E-9). Does <strong>not</strong>
134     * represent wall clock time, but is used to measure elapsed time. Provides
135     * nanosecond precision, but not accuracy.
136     * 
137     * @see {@link System#nanoTime()}
138     */
139    private long stopTimeNanos;
140    /**
141     * Number of concurrent timers performing this task. This value is set by an
142     * external source; therefore, the value may not accurately reflect the true
143     * number of concurrent tasks.
144     */
145    private int concurrent;
146 
147    /**
148     * Current status of timer.
149     */
150    private TimerStatus status;
151 
152    /**
153     * What to do when the timer stops. Default is to do nothing.
154     */
155    private transient TimeRecorder timeRecorder;
156 
157    /**
158     * Name of thread under which the timer creation was running.
159     */
160    private final String threadName;
161 
162    /**
163     * Optional list of domain specific objects provided by user. These will be
164     * displayed if provided.
165     */
166    private TimerNotes notes;
167 
168    /**
169     * Optional level for this timer. May be used to filter or control processing of timers.
170     */
171    private final TimerLevel timerLevel;
172 
173    /**
174         * Listener to notify when this timer is stopped.
175         */
176    private transient TimerStoppedListener completionListener;
177 
178    /**
179     * Construct an instance of Timer for the given task and start the timer.
180     * 
181     * @param taskName name of task being timed. 
182     */
183    public Timer(String taskName) {
184        this(taskName, false, null, null);
185    }
186    
187    /**
188     * Construct an instance of Timer for the given task and start the timer.
189     * 
190     * @param taskName name of task being timed. 
191     * @param timerLevel filter level of this timer.
192     */
193    public Timer(String taskName, TimerLevel timerLevel) {
194        this(taskName, false, null, timerLevel);
195    }
196 
197    /**
198     * Construct an instance of Timer for the given task and optionally delay
199     * the start of the timer.
200     * 
201     * @param taskName name of task being timed. 
202     * @param delayStart true if starting of timer should be delayed.  Will not start recording until {@link #start()} is invoked.
203     * @param timerLevel filter level of this timer.
204     */
205    public Timer(String taskName, boolean delayStart, TimerLevel timerLevel) {
206        this(taskName, delayStart, null, timerLevel);
207    }
208    
209    /**
210     * Construct an instance of Timer for the given task and optionally delay
211     * the start of the timer.
212     * 
213     * @param taskName name of task being timed. 
214     * @param delayStart true if starting of timer should be delayed.  Will not start recording until {@link #start()} is invoked.
215     * @param timeRecorder instance used to record this timer upon completion.
216     */
217    public Timer(String taskName, boolean delayStart, TimeRecorder timeRecorder) {
218        this(taskName, delayStart, timeRecorder, null);
219    }
220    /**
221     * Construct an instance of Timer for the given task and optionally delay
222     * the start of the timer.
223     * 
224     * @param taskName name of task being timed. 
225     * @param delayStart true if starting of timer should be delayed.  Will not start recording until {@link #start()} is invoked.
226     * @param timeRecorder instance used to record this timer upon completion.
227     * @param timerLevel filter level of this timer.
228     */
229    public Timer(String taskName, boolean delayStart, TimeRecorder timeRecorder, TimerLevel timerLevel) {
230        this.status = TimerStatus.INITIALIZED;
231        this.taskName = taskName;
232        this.timeRecorder = timeRecorder;
233        this.threadName = Thread.currentThread().getName();
234        this.timerLevel = timerLevel;
235        if (!delayStart) {
236            start();
237        }
238    }
239 
240    /**
241     * Used by internal method constructing object from CSV file.
242     * 
243     * @param taskName name of task being timed. 
244     * @param threadName name of thread in which timer was created.
245     */
246    private Timer(String taskName, String threadName) {
247        this(taskName, threadName, null);
248    }
249    
250    /**
251     * Used by internal method constructing object from CSV file.
252     * 
253     * @param taskName name of task being timed. 
254     * @param threadName name of thread in which timer was created.
255     * @param timerLevel filter level of this timer.
256     */
257    private Timer(String taskName, String threadName, TimerLevel timerLevel) {
258        this.taskName = taskName;
259        this.threadName = threadName;
260        this.timerLevel = timerLevel;
261    }
262 
263    /**
264     * Start the time recording if not already started.
265     */
266    public void start() {
267        if (status == TimerStatus.INITIALIZED) {
268            startTimeNanos = System.nanoTime();
269            startTimeMillis = System.currentTimeMillis();
270            status = TimerStatus.RUNNING;
271        }
272    }
273 
274    /**
275     * Stop the current time recording. Timer must be running and can be stopped
276     * one time only. If stopped multiple times,only the first will be recorded.
277     * <p>
278     * The timer entering the stopped state triggers the optional reporting
279     * processing such as logging and firing
280     * {@link TimerStoppedListener#timerStopped(TimerStoppedEvent)}
281     * 
282     * @return the elapsed time in nanoseconds.
283     */
284    public long stop() {
285        if (status == TimerStatus.RUNNING) {
286            stopTimeNanos = System.nanoTime();
287            status = TimerStatus.STOPPED;
288            if (timeRecorder != null) {
289                timeRecorder.record(this);
290            }
291 
292            if (completionListener != null) {
293                completionListener.timerStopped(new TimerStoppedEvent(this));
294            }
295        }
296 
297        return stopTimeNanos - startTimeNanos;
298    }
299 
300    /**
301     * Stop the current time recording. Timer must be running and can be stopped
302     * one time only. If stopped multiple times,only the first will be recorded.
303     * <p>
304     * The timer entering the stopped state triggers the optional reporting
305     * processing such as logging and firing
306     * {@link TimerStoppedListener#timerStopped(TimerStoppedEvent)}
307     * <p>
308     * This overload of the stop method allows the notes to be placed on the
309     * timer for inclusion in post processing and/or logging fired from this
310     * action.
311     * <p>
312     * The notes are provided via the array of domain specific objects. These
313     * values, if provided, will be displayed with the other timer values. As
314     * all objects are stored as {@link Object}, any primitive passed in will be
315     * auto boxed.
316     * <p>
317     * <strong>Note:</strong> Adding of notes incurs a cost due to auto boxing
318     * of primitives and array creation for the variable arguments parameter.
319     * This cost should be small, but for recording of very small intervals,
320     * this could affect the measurements. If the notes are not needed for post
321     * processing/logging, adding of notes should be done using
322     * {@link #setNotes(Object...)}.
323     * <p>
324     * <strong>Note:</strong> This method will overwrite any current notes
325     * already present.
326     * 
327     * @param notes
328     *            List of domain specific values to store with the timer.
329     * @return the elapsed time in nanoseconds.
330     */
331    public long stop(Object... notes) {
332        return stop(false, notes);
333    }
334 
335    /**
336     * Stop the current time recording. Timer must be running and can be stopped
337     * one time only. If stopped multiple times,only the first will be recorded.
338     * <p>
339     * The timer entering the stopped state triggers the optional reporting
340     * processing such as logging and firing
341     * {@link TimerStoppedListener#timerStopped(TimerStoppedEvent)}
342     * <p>
343     * This overload of the stop method allows the notes to be placed on the
344     * timer for inclusion in post processing and/or logging fired from this
345     * action.
346     * <p>
347     * The notes are provided via the array of domain specific objects. These
348     * values, if provided, will be displayed with the other timer values. As
349     * all objects are stored as {@link Object}, any primitive passed in will be
350     * auto boxed.
351     * <p>
352     * <strong>Note:</strong> Adding of notes incurs a cost due to auto boxing
353     * of primitives and array creation for the variable arguments parameter.
354     * This cost should be small, but for recording of very small intervals,
355     * this could affect the measurements. If the notes are not needed for post
356     * processing/logging, adding of notes should be done using
357     * {@link #setNotes(Object...)} or {@link #setKeyedNotes(Object...)}.
358     * <p>
359     * <strong>Note:</strong> This method will overwrite any current notes
360     * already present.
361     * 
362     * @param keyed
363     *            true if notes are provided as key/value pairs (e.g.
364     *            key1,val1,key2,val2...), false if all notes are values (e.g.,
365     *            val1,val2,val3...).
366     * @param notes
367     *            list of either key/value pairs or values.
368     * 
369     * @return the elapsed time in nanoseconds.
370     */
371    public long stop(Boolean keyed, Object... notes) {
372        if (status == TimerStatus.RUNNING) {
373            stopTimeNanos = System.nanoTime();
374            status = TimerStatus.STOPPED;
375            this.notes = keyed ? new KeyedTimerNotes(notes) : new TimerNoteList(notes);
376            if (timeRecorder != null) {
377                timeRecorder.record(this);
378            }
379 
380            if (completionListener != null) {
381                completionListener.timerStopped(new TimerStoppedEvent(this));
382            }
383        }
384 
385        return stopTimeNanos - startTimeNanos;
386    }
387 
388    /**
389     * Indicate whether or not to log the results of the timer upon completion.
390     * See {@link TimeRecorder} for more information. Has no effect after the
391     * timer has been stopped.
392     * 
393     * @param logType
394     */
395    public void setTimeRecorder(TimeRecorder timeRecorder) {
396        this.timeRecorder = timeRecorder;
397    }
398 
399    /**
400     * Return the task name associated with this timer.
401     * 
402     * @return the taskName
403     */
404    public String getTaskName() {
405        return taskName;
406    }
407 
408    /**
409     * Return the name of the thread under which this timer was created.
410     * 
411     * @return the taskName
412     */
413    public String getThreadName() {
414        return threadName;
415    }
416 
417    /**
418     * Return the start time in milliseconds. This time represents wall clock
419     * time. Timer must be be running or stopped prior to invoking this method.
420     * 
421     * @return the startTimeMillis
422     */
423    public long getStartTimeMillis() {
424        if (status == TimerStatus.INITIALIZED) {
425            throw new IllegalStateException("Timer has not been started");
426        }
427        return startTimeMillis;
428    }
429 
430    /**
431     * Return the start time in nanoseconds. This time does not represent wall
432     * clock time and is used only to determine elapsed time. Timer must be be
433     * running or stopped prior to invoking this method.
434     * 
435     * @return the startTimeNanos
436     */
437    public long getStartTimeNanos() {
438        if (status == TimerStatus.INITIALIZED) {
439            throw new IllegalStateException("Timer has not been started");
440        }
441        return startTimeNanos;
442    }
443 
444    /**
445     * Return the stop time in nanoseconds. This time does not represent wall
446     * clock time and is used only to determine elapsed time. Timer must be
447     * stopped prior to invoking this method.
448     * 
449     * @return the stopTimeNanos
450     */
451    public long getStopTimeNanos() {
452        if (status != TimerStatus.STOPPED) {
453            throw new IllegalStateException("Timer has not been started");
454        }
455        return stopTimeNanos;
456    }
457 
458    /**
459     * Return the number of concurrent timers recorded for a task. This value is
460     * set by an external source; therefore, the value may not accurately
461     * reflect the true number of concurrent tasks.
462     * 
463     * @return the number of concurrent timers recorded for a task.
464     */
465    public int getConcurrent() {
466        return concurrent;
467    }
468 
469    /**
470     * Sets the number of concurrent timers recorded for a task.
471     * 
472     * @param concurrent
473     *            the number of other timers with for this task at the time of
474     *            its creation.
475     */
476    public void setConcurrent(int concurrent) {
477        this.concurrent = concurrent;
478    }
479 
480    /**
481     * Return a string format of the current timer including the task name,
482     * start time, elapsed time, and any notes associated with the timer. If the
483     * timer has not yet completed, a value of -1 will be returned for the
484     * elapsed time.
485     * 
486     * @see #setNotes(Object...)
487     */
488    @Override
489    public String toString() {
490        StringBuilder sb = new StringBuilder(100);
491        sb.append("Task: ").append(taskName);
492        sb.append(" Start: ").append(new Date(startTimeMillis));
493        sb.append(" Elapsed (ms): ").append(getElapsedMillis());
494        sb.append(" Elapsed (ns): ").append(getElapsedNanos());
495        if (notes != null) {
496            sb.append(" Notes: ");
497            for (int i = 0; i < notes.getLength(); i++) {
498                if (i > 0) {
499                    sb.append(",");
500                }
501                sb.append(notes.getFormattedNote(i));
502            }
503        }
504        return sb.toString();
505    }
506 
507    /**
508     * Return a CSV format of the current timer in the format: start time in
509     * milliseconds, task name, thread name, elapsed milliseconds, elapsed
510     * nanoseconds, concurrent count, and any associated notes. If the timer has
511     * not yet completed, a value of -1 will be returned for the elapsed time.
512     * 
513     * @see #setConcurrent(int)
514     * @see #setNotes(Object...)
515     */
516    public String toCsv() {
517        StringBuilder sb = new StringBuilder(100);
518        sb.append(startTimeMillis);
519        sb.append(",").append(taskName);
520        sb.append(",").append(threadName);
521        sb.append(",").append(getElapsedMillis());
522        sb.append(",").append(getElapsedNanos());
523        sb.append(",").append(getConcurrent());
524        sb.append(",");
525        if (notes != null) {
526            sb.append(notes.toSingleValue());
527        }
528        return sb.toString();
529    }
530 
531    /**
532     * Return a CSV formatted string providing a header for the entries that
533     * will be written if CSV logging is enabled. This may be used at the top of
534     * a CSV file and will always match the format of the records.
535     * 
536     * @return
537     */
538    public static String getCsvHeader() {
539        StringBuilder sb = new StringBuilder(100);
540        sb.append("start_time_ms");
541        sb.append(",").append("task");
542        sb.append(",").append("thread");
543        sb.append(",").append("elapsed_ms");
544        sb.append(",").append("elapsed_ns");
545        sb.append(",").append("concurrent");
546        sb.append(",").append("notes");
547        return sb.toString();
548    }
549 
550    /**
551     * Create a {@link Timer} and populate the member variables using values
552     * extracted from the CSV record. This {@link Timer} will be in a
553     * {@link TimerStatus#STOPPED} state. Also, no handlers or completion
554     * listeners will be added.
555     * 
556     * @param timerAsCsv
557     * @return Timer from which values may be extracted.
558     */
559    public static Timer fromCsv(String timerAsCsv) {
560        String[] values = timerAsCsv.split(",");
561        Timer timer = new Timer(values[1], values[2]);
562        timer.status = TimerStatus.STOPPED;
563        timer.startTimeMillis = Long.parseLong(values[0].trim());
564        timer.startTimeNanos = 0L; // nanoseconds start and stop are relative to
565                                   // each other, not the wall clock.
566        timer.stopTimeNanos = Long.parseLong(values[4].trim());
567        timer.concurrent = Integer.parseInt(values[5].trim());
568        if (values.length > 6) {
569                timer.notes = TimerNotesParser.parse(values[6]);
570        }
571 
572        return timer;
573    }
574 
575    /**
576     * Return the elapsed time for this task in nanoseconds. If the task is not
577     * yet complete, the value returned depends on the snapshotTime argument. If
578     * snapshotTime is true, then the value returned will be the current time -
579     * start time, otherwise a value of -1 will be returned.
580     * 
581     * @return elapsed time in nanoseconds or -1 if task is not yet complete and
582     *         the snapshotTime indicator is set to false.
583     */
584    public long getElapsedNanos(boolean snapshotTime) {
585        switch (status) {
586        case INITIALIZED:
587            throw new IllegalStateException("Timer has not been started");
588 
589        case RUNNING:
590            return snapshotTime ? System.nanoTime() - startTimeNanos : -1;
591 
592        default:
593            return stopTimeNanos - startTimeNanos;
594        }
595 
596    }
597 
598    /**
599     * Return the elapsed time for this task in nanoseconds if the timer has
600     * been stopped. For currently running time use
601     * {@link #getElapsedNanos(boolean)} with a value of true.
602     * 
603     * @return elapsed time in nanoseconds or -1 if task is not yet complete.
604     */
605    public long getElapsedNanos() {
606        return getElapsedNanos(false);
607    }
608 
609    /**
610     * Return the elapsed time for this task in milliseconds. The task must have
611     * been started prior to invoking this method.
612     * 
613     * @return elapsed time in milliseconds or -1 if task is not yet complete.
614     */
615    public long getElapsedMillis() {
616        if (status == TimerStatus.INITIALIZED) {
617            throw new IllegalStateException("Timer has not been started");
618        }
619        if (status == TimerStatus.STOPPED) {
620            return (stopTimeNanos - startTimeNanos) / 1000000;
621        } else {
622            return -1;
623        }
624    }
625 
626    /**
627     * Return the current state of the timer. See {@link TimerStatus} for the
628     * different states.
629     * 
630     * @return
631     * @see TimerStatus
632     */
633    public TimerStatus getStatus() {
634        return status;
635    }
636 
637    /**
638     * Return array of domain specific objects.
639     */
640    public TimerNotes getNotes() {
641        return notes;
642    }
643 
644    /**
645     * Optional array of domain specific objects. These will be displayed if
646     * provided. As all objects are stored as {@link Object}, any primitive
647     * passed in will be auto boxed.
648     * <p>
649     * The setNotes method is destructive in that it will overwrite any current
650     * notes already present. The notes should be set once all of the applicable
651     * notes have been accumulated.
652     */
653    public void setNotes(Object... notes) {
654        this.notes = new TimerNoteList(notes);
655    }
656 
657    /**
658     * Optional array of domain specific objects. These will be displayed if
659     * provided. As all objects are stored as {@link Object}, any primitive
660     * passed in will be auto boxed.
661     * <p>
662     * The setNotes method is destructive in that it will overwrite any current
663     * notes already present. The notes should be set once all of the applicable
664     * notes have been accumulated.
665     */
666    public void setKeyedNotes(Object... notes) {
667        this.notes = new KeyedTimerNotes(notes);
668    }
669 
670    public static enum TimerStatus {
671        INITIALIZED, RUNNING, STOPPED;
672    }
673 
674    /**
675     * Returns true if the timer is currently in a running state.
676     * 
677     * @return
678     */
679    public boolean isRunning() {
680        return status == TimerStatus.RUNNING;
681    }
682 
683    /**
684     * Returns true if the timer has been stopped.
685     * 
686     * @return
687     */
688    public boolean isStopped() {
689        return status == TimerStatus.STOPPED;
690    }
691 
692    /**
693     * Set the single completion listener for this timer. If one already exists,
694     * it will be overridden.
695     * <p>
696     * <strong>NOTE:</strong> As the completion of the timer is most likely in
697     * the current processing, the listener should return as quickly as possible
698     * and with preferably no synchronization. For longer processing, look at
699     * the {@link ActionChain} or {@link QueuedTimeRecorder} which provide a queue 
700     * for processing timers off of the main processing thread.
701     * 
702     * @param completionListener
703     *            Completion listener or null to remove current listener.
704     */
705    public void setCompletionListener(TimerStoppedListener completionListener) {
706        this.completionListener = completionListener;
707    }
708 
709    /**
710     * Update the status subject to state constraints.
711     * 
712     * @param proposedStatus
713     * @return the current status.
714     */
715    protected TimerStatus updateStatus(TimerStatus proposedStatus) {
716        switch (proposedStatus) {
717        case RUNNING:
718            if (status != TimerStatus.STOPPED) {
719                status = TimerStatus.RUNNING;
720            }
721            break;
722        case STOPPED:
723            // Just set the status as either it is already Stopped or it is
724            // allowed.
725            status = proposedStatus;
726            break;
727        case INITIALIZED:
728            // Nothing to do for initialized. Either it is already there or not
729            // permitted.
730            break;
731        }
732        return status;
733    }
734 
735    /**
736     * Return the timer level associated with this timer. May be null
737     * @return the timerLevel or null if no level assigned to timer.
738     */
739    public TimerLevel getTimerLevel() {
740        return timerLevel;
741    }
742}

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