001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.util;
018:
019: import java.text.NumberFormat;
020: import java.util.LinkedList;
021: import java.util.List;
022:
023: /**
024: * Simple stop watch, allowing for timing of a number of tasks,
025: * exposing total running time and running time for each named task.
026: *
027: * <p>Conceals use of <code>System.currentTimeMillis()</code>, improving the
028: * readability of application code and reducing the likelihood of calculation errors.
029: *
030: * <p>Note that this object is not designed to be thread-safe and does not
031: * use synchronization.
032: *
033: * <p>This class is normally used to verify performance during proof-of-concepts
034: * and in development, rather than as part of production applications.
035: *
036: * @author Rod Johnson
037: * @author Juergen Hoeller
038: * @since May 2, 2001
039: */
040: public class StopWatch {
041:
042: /**
043: * Identifier of this stop watch.
044: * Handy when we have output from multiple stop watches
045: * and need to distinguish between them in log or console output.
046: */
047: private final String id;
048:
049: private boolean keepTaskList = true;
050:
051: /** List of TaskInfo objects */
052: private final List taskList = new LinkedList();
053:
054: /** Start time of the current task */
055: private long startTimeMillis;
056:
057: /** Is the stop watch currently running? */
058: private boolean running;
059:
060: /** Name of the current task */
061: private String currentTaskName;
062:
063: private TaskInfo lastTaskInfo;
064:
065: private int taskCount;
066:
067: /** Total running time */
068: private long totalTimeMillis;
069:
070: /**
071: * Construct a new stop watch. Does not start any task.
072: */
073: public StopWatch() {
074: this .id = "";
075: }
076:
077: /**
078: * Construct a new stop watch with the given id.
079: * Does not start any task.
080: * @param id identifier for this stop watch.
081: * Handy when we have output from multiple stop watches
082: * and need to distinguish between them.
083: */
084: public StopWatch(String id) {
085: this .id = id;
086: }
087:
088: /**
089: * Determine whether the TaskInfo array is built over time. Set this to
090: * "false" when using a StopWatch for millions of intervals, or the task
091: * info structure will consume excessive memory. Default is "true".
092: */
093: public void setKeepTaskList(boolean keepTaskList) {
094: this .keepTaskList = keepTaskList;
095: }
096:
097: /**
098: * Start an unnamed task. The results are undefined if {@link #stop()}
099: * or timing methods are called without invoking this method.
100: * @see #stop()
101: */
102: public void start() throws IllegalStateException {
103: start("");
104: }
105:
106: /**
107: * Start a named task. The results are undefined if {@link #stop()}
108: * or timing methods are called without invoking this method.
109: * @param taskName the name of the task to start
110: * @see #stop()
111: */
112: public void start(String taskName) throws IllegalStateException {
113: if (this .running) {
114: throw new IllegalStateException(
115: "Can't start StopWatch: it's already running");
116: }
117: this .startTimeMillis = System.currentTimeMillis();
118: this .running = true;
119: this .currentTaskName = taskName;
120: }
121:
122: /**
123: * Stop the current task. The results are undefined if timing
124: * methods are called without invoking at least one pair
125: * {@link #start()} / {@link #stop()} methods.
126: * @see #start()
127: */
128: public void stop() throws IllegalStateException {
129: if (!this .running) {
130: throw new IllegalStateException(
131: "Can't stop StopWatch: it's not running");
132: }
133: long lastTime = System.currentTimeMillis()
134: - this .startTimeMillis;
135: this .totalTimeMillis += lastTime;
136: this .lastTaskInfo = new TaskInfo(this .currentTaskName, lastTime);
137: if (this .keepTaskList) {
138: this .taskList.add(lastTaskInfo);
139: }
140: ++this .taskCount;
141: this .running = false;
142: this .currentTaskName = null;
143: }
144:
145: /**
146: * Return whether the stop watch is currently running.
147: */
148: public boolean isRunning() {
149: return this .running;
150: }
151:
152: /**
153: * Return the time taken by the last task.
154: */
155: public long getLastTaskTimeMillis() throws IllegalStateException {
156: if (this .lastTaskInfo == null) {
157: throw new IllegalStateException(
158: "No tests run: can't get last interval");
159: }
160: return this .lastTaskInfo.getTimeMillis();
161: }
162:
163: /**
164: * Return the total time in milliseconds for all tasks.
165: */
166: public long getTotalTimeMillis() {
167: return totalTimeMillis;
168: }
169:
170: /**
171: * Return the total time in seconds for all tasks.
172: */
173: public double getTotalTimeSeconds() {
174: return totalTimeMillis / 1000.0;
175: }
176:
177: /**
178: * Return the number of tasks timed.
179: */
180: public int getTaskCount() {
181: return taskCount;
182: }
183:
184: /**
185: * Return an array of the data for tasks performed.
186: */
187: public TaskInfo[] getTaskInfo() {
188: if (!this .keepTaskList) {
189: throw new UnsupportedOperationException(
190: "Task info is not being kept!");
191: }
192: return (TaskInfo[]) this .taskList
193: .toArray(new TaskInfo[this .taskList.size()]);
194: }
195:
196: /**
197: * Return a short description of the total running time.
198: */
199: public String shortSummary() {
200: return "StopWatch '" + this .id + "': running time (millis) = "
201: + getTotalTimeMillis();
202: }
203:
204: /**
205: * Return a string with a table describing all tasks performed.
206: * For custom reporting, call getTaskInfo() and use the task info directly.
207: */
208: public String prettyPrint() {
209: StringBuffer sb = new StringBuffer(shortSummary());
210: sb.append('\n');
211: if (!this .keepTaskList) {
212: sb.append("No task info kept");
213: } else {
214: TaskInfo[] tasks = getTaskInfo();
215: sb.append("-----------------------------------------\n");
216: sb.append("ms % Task name\n");
217: sb.append("-----------------------------------------\n");
218: NumberFormat nf = NumberFormat.getNumberInstance();
219: nf.setMinimumIntegerDigits(5);
220: nf.setGroupingUsed(false);
221: NumberFormat pf = NumberFormat.getPercentInstance();
222: pf.setMinimumIntegerDigits(3);
223: pf.setGroupingUsed(false);
224: for (int i = 0; i < tasks.length; i++) {
225: sb.append(nf.format(tasks[i].getTimeMillis()) + " ");
226: sb.append(pf.format(tasks[i].getTimeSeconds()
227: / getTotalTimeSeconds())
228: + " ");
229: sb.append(tasks[i].getTaskName() + "\n");
230: }
231: }
232: return sb.toString();
233: }
234:
235: /**
236: * Return an informative string describing all tasks performed
237: * For custom reporting, call <code>getTaskInfo()</code> and use the task info directly.
238: */
239: public String toString() {
240: StringBuffer sb = new StringBuffer(shortSummary());
241: if (this .keepTaskList) {
242: TaskInfo[] tasks = getTaskInfo();
243: for (int i = 0; i < tasks.length; i++) {
244: sb.append("; [" + tasks[i].getTaskName() + "] took "
245: + tasks[i].getTimeMillis());
246: long percent = Math.round((100.0 * tasks[i]
247: .getTimeSeconds())
248: / getTotalTimeSeconds());
249: sb.append(" = " + percent + "%");
250: }
251: } else {
252: sb.append("; no task info kept");
253: }
254: return sb.toString();
255: }
256:
257: /**
258: * Inner class to hold data about one task executed within the stop watch.
259: */
260: public static class TaskInfo {
261:
262: private final String taskName;
263:
264: private final long timeMillis;
265:
266: private TaskInfo(String taskName, long timeMillis) {
267: this .taskName = taskName;
268: this .timeMillis = timeMillis;
269: }
270:
271: /**
272: * Return the name of this task.
273: */
274: public String getTaskName() {
275: return taskName;
276: }
277:
278: /**
279: * Return the time in milliseconds this task took.
280: */
281: public long getTimeMillis() {
282: return timeMillis;
283: }
284:
285: /**
286: * Return the time in seconds this task took.
287: */
288: public double getTimeSeconds() {
289: return timeMillis / 1000.0;
290: }
291: }
292:
293: }
|