View Javadoc

1   /*
2    * $Id: Stopwatch.java,v 1.2 2006/03/06 11:30:53 azzazzel Exp $
3    *
4    * Copyright 2006 Commsen International
5    * 
6    * Licensed under the Common Public License, Version 1.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    * 
10   *      http://www.opensource.org/licenses/cpl1.0.txt
11   * 
12   * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
13   * EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS 
14   * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
15   *
16   */
17  package com.commsen.stopwatch;
18  
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.net.URI;
25  import java.net.URISyntaxException;
26  import java.util.Arrays;
27  import java.util.Properties;
28  
29  import org.apache.log4j.Logger;
30  
31  import com.commsen.stopwatch.engines.DefaultStopwatchEngine;
32  import com.commsen.stopwatch.jmx.StopwatchAgent;
33  import com.commsen.stopwatch.storages.DefaultHSQLInMemoryStorage;
34  import com.commsen.stopwatch.storages.StorageManager;
35  import com.commsen.stopwatch.util.ReportComparator;
36  
37  /***
38   * Stopwatch allows you to measure performance of any given piece of code. It's basic usage is as
39   * follows:
40   * 
41   * <code>
42   * <pre>
43   * .....
44   * long swId = Stopwatch.start(&quot;group&quot;, &quot;label&quot;);
45   * // some code to be measured
46   * Stopwatch.stop(swId);
47   * ...
48   * </pre>
49   *</code>
50   * 
51   * since version 0.4 there is also {@link Meter} object
52   * 
53   * <code>
54   * <pre>
55   * .....
56   * Meter meter = Stopwatch.startMeter(&quot;group&quot;, &quot;label&quot;);
57   * // some code to be measured
58   * meter.stop();
59   * ...
60   * </pre>
61   *</code>
62   * 
63   * To skip already started mensuration (for example if an Exception is thrown) something similar to
64   * following code may be used:
65   * 
66   * <code>
67   * <pre>
68   * .....
69   * long swId = Stopwatch.start(&quot;group&quot;, &quot;label&quot;);
70   * try {
71   *    // some code to be measured
72   * } catch (Exception) { 
73   *    Stopwatch.skip(swId);
74   * } finally {
75   *    Stopwatch.stop(swId);
76   * }
77   * ...
78   * </pre>
79   *</code>
80   * 
81   * or alternatively (since version 0.4) use {@link Meter} object
82   * 
83   * <code>
84   * <pre>
85   * .....
86   * Meter meter = Stopwatch.startMeter(&quot;group&quot;, &quot;label&quot;);
87   * try {
88   *    // some code to be measured
89   * } catch (Exception) { 
90   *    meter.cancel();
91   * } finally {
92   *    meter.stop();
93   * }
94   * ...
95   * </pre>
96   *</code>
97   * 
98   * <p>
99   * <b>By default Stopwatch is not active!</b> It means all calls to {@link #start(String, String)},
100  * {@link #skip(long)} and {@link #stop(long)} methods are simply ignored. To activate Stopwatch do
101  * one of the following:
102  * <ul>
103  * <li>explicitly call Stopwatch.setActive(true)</li>
104  * <li>pass <code>-Dcom.commsen.stopwatch.activeOnStart=true</code> JVM parameter</li>
105  * <li>create "stopwatch.properties" file on classpath and set <code>activeOnStart=true</code></li>
106  * </ul>
107  * Stopwatch can also be activated/deactivated at runtime via JMX, RMI, etc.
108  * </p>
109  * 
110  * @author Milen Dyankov
111  * 
112  */
113 public class Stopwatch {
114 
115 	/***
116 	 * Logger for this class
117 	 */
118 	private static final Logger log = Logger.getLogger(Stopwatch.class);
119 
120 	public static final String SYSTEM_PROPERTIES_PREFIX = "com.commsen.stopwatch.";
121 	public static final String PROPERTY_ACTIVE = "activeOnStart";
122 	public static final String PROPERTY_DEBUG = "debugEnabled";
123 	public static final String PROPERTY_ENGINE = "engine";
124 	public static final String PROPERTY_ENGINE_PROPERTIES = "engine.properties";
125 	public static final String PROPERTY_STORAGE = "storage";
126 	public static final String PROPERTY_STORAGE_PROPERTIES = "storage.properties";
127 	public static final String PROPERTY_MODE = "persistenceMode";
128 	public static final String PROPERTY_JMX_MANAGED = "jmxManaged";
129 	public static final String PROPERTY_MBEAN_SERVER_NAME = "MBeanServer";
130 
131 	public static final String DEFAULT_ENGINE = DefaultStopwatchEngine.class.getName();
132 	public static final String DEFAULT_ENGINE_PROPERTIES = "classpath:stopwatch-engine.properties";
133 	public static final String DEFAULT_STORAGE = DefaultHSQLInMemoryStorage.class.getName();
134 	public static final String DEFAULT_STORAGE_PROPERTIES = "classpath:stopwatch-storage.properties";
135 	public static final int DEFAULT_MODE = StorageManager.DELAYED_MODE;
136 
137 	/***
138 	 * Indicates if stopwatch is active. When <code>false</code> all calls to
139 	 * {@link #start(String, String)} and {@link #stop(long)} are ignored.
140 	 */
141 	private static boolean active;
142 
143 	/***
144 	 * Indicates if stopwatch was properly initialized. If <code>false</code> then Stopwatch can
145 	 * not be activated
146 	 */
147 	private static boolean initialised;
148 
149 	/***
150 	 * Indicates if stopwatch should produce debug information
151 	 */
152 	private static boolean debugEnabled;
153 
154 	/***
155 	 * Indicates if stopwatch should be managed via JMX
156 	 */
157 	private static boolean jmxManaged;
158 
159 	/***
160 	 * The name of the MBean server to register manager with
161 	 */
162 	private static String mBeanServerName;
163 
164 	/***
165 	 * Holds Stopwatch's properties (if any)
166 	 */
167 	private static Properties stopwatchProperties = new Properties();
168 
169 	/***
170 	 * Holds reference to current stopwatch engine
171 	 */
172 	private static StopwatchEngine engine;
173 
174 	/***
175 	 * Holds reference to current stopwatch JMX agent
176 	 */
177 	private static StopwatchAgent agent;
178 
179 	/***
180 	 * Current persistence mode
181 	 */
182 	private static String persistenceMode;
183 
184 	/*
185 	 * Static initialization
186 	 */
187 	static {
188 		init();
189 	}
190 
191 
192 	/***
193 	 * Starts new measurement. This method will do nothing and simply return a negative long if
194 	 * Stopwatch is not active.
195 	 * 
196 	 * @param group the name of the group this measurement should be placed in
197 	 * @param label how this measurement should be labeled
198 	 * @return Unique ID representing current measurement.
199 	 */
200 	public static long start(String group, String label) {
201 		if (!isActive()) {
202 			if (isDebug()) {
203 				log.debug("Stopwatch.start(" + group + "," + label + ") ignored. Stopwatch is not active!");
204 			}
205 			return -1;
206 		}
207 		return engine.begin(group, label);
208 	}
209 
210 
211 	/***
212 	 * Starts new measurement and provides a {@link Meter} which can be later on stopped ({@link Meter#stop()})
213 	 * or canceled ({@link Meter#cancel()}).
214 	 * 
215 	 * @param group the name of the group this measurement should be placed in
216 	 * @param label how this measurement should be labeled
217 	 * @return a {@link Meter} which can be later on stopped ({@link Meter#stop()}) or canceled ({@link Meter#cancel()})
218 	 * @since 0.4
219 	 */
220 	public static Meter startMeter(String group, String label) {
221 		return new Meter(start(group, label));
222 	}
223 
224 
225 	/***
226 	 * Stop measurement identified by <code>id</code>. This method will do nothing if Stopwatch
227 	 * is not active or measurement has been skipped already.
228 	 * 
229 	 * @param id which measurement to stop
230 	 */
231 	public static void stop(long id) {
232 		if (!isActive()) {
233 			if (isDebug()) {
234 				log.debug("Stopwatch.stop(" + id + ") ignored. Stopwatch is not active!");
235 			}
236 			return;
237 		}
238 		engine.end(id);
239 	};
240 
241 
242 	/***
243 	 * Skip measurement identified by <code>id</code>. Method should be called if for some reason
244 	 * current measurement should be skipped. Default stopwatch engine will delete any information
245 	 * related to this <code>id</code> but other engines may behave differently (for example to
246 	 * provide a "skipped measurements" report).
247 	 * 
248 	 * This method will do nothing if Stopwatch is not active or measurement has been stopped
249 	 * already.
250 	 * 
251 	 * @param id which measurement to stop
252 	 */
253 	public static void skip(long id) {
254 
255 		if (!isActive()) {
256 			if (isDebug()) {
257 				log.debug("Stopwatch.skip(" + id + ") ignored. Stopwatch is not active!");
258 			}
259 			return;
260 		}
261 		engine.skip(id);
262 	};
263 
264 
265 	/***
266 	 * Generates an array of reports which contains exactly 1 element for each combination of
267 	 * <b>group</b> and <b>label</b> If there is no enough data to produce reports, this method
268 	 * returns <code>null</code>
269 	 * 
270 	 * @return array of reports of <code>null</code>.
271 	 */
272 	public static Report[] getAllReports() {
273 		return getAllReports(null);
274 	}
275 
276 
277 	/***
278 	 * Generates an array of reports which contains exactly 1 element for each combination of
279 	 * <b>group</b> and <b>label</b> If there is no enough data to produce reports, this method
280 	 * returns <code>null</code>
281 	 * 
282 	 * @return array of reports of <code>null</code>.
283 	 */
284 	public static Report[] getAllReports(ReportComparator comparator) {
285 
286 		if (!Stopwatch.initialised) {
287 			log.warn("Stopwatch not propery initialised! Call to getAllReports() was ignored! Try to reset Stopwatch.");
288 			return new Report[0];
289 		}
290 
291 		Report[] reports = engine.getStorage().getReports();
292 		Arrays.sort(reports, comparator);
293 		return reports;
294 	}
295 
296 
297 	/***
298 	 * 
299 	 * @return
300 	 */
301 	public static Report[] getAllByGroupReports() {
302 		return getAllByGroupReports(null);
303 	}
304 
305 
306 	/***
307 	 * 
308 	 * @return
309 	 */
310 	public static Report[] getAllByGroupReports(ReportComparator comparator) {
311 
312 		if (!Stopwatch.initialised) {
313 			log.warn("Stopwatch not propery initialised! Call to getAllByGroupReports() was ignored! Try to reset Stopwatch.");
314 			return new Report[0];
315 		}
316 
317 		Report[] reports = engine.getStorage().getAllByGroupReports();
318 		Arrays.sort(reports, comparator);
319 		return reports;
320 
321 	}
322 
323 
324 	/***
325 	 * 
326 	 * @return
327 	 */
328 	public static Report[] getAllByLabelReports() {
329 		return getAllByLabelReports(null);
330 	}
331 
332 
333 	/***
334 	 * 
335 	 * @return
336 	 */
337 	public static Report[] getAllByLabelReports(ReportComparator comparator) {
338 
339 		if (!Stopwatch.initialised) {
340 			log.warn("Stopwatch not propery initialised! Call to getAllByLabelReports() was ignored! Try to reset Stopwatch.");
341 			return new Report[0];
342 		}
343 
344 		Report[] reports = engine.getStorage().getAllByLabelReports();
345 		Arrays.sort(reports, comparator);
346 		return reports;
347 	}
348 
349 
350 	/***
351 	 * Generates an array of reports which contains exactly 1 element for each <b>group</b> If
352 	 * there is no enough data to produce the report, this method returns <code>null</code>
353 	 * 
354 	 * @param group the group for which report should be generated
355 	 * @return array of reports of <code>null</code>.
356 	 */
357 	public static Report[] getGroupReports(String group) {
358 		return getGroupReports(group, null);
359 	}
360 
361 
362 	/***
363 	 * Generates an array of reports which contains exactly 1 element for each <b>group</b> If
364 	 * there is no enough data to produce the report, this method returns <code>null</code>
365 	 * 
366 	 * @param group the group for which report should be generated
367 	 * @return array of reports of <code>null</code>.
368 	 */
369 	public static Report[] getGroupReports(String group, ReportComparator comparator) {
370 		if (!Stopwatch.initialised) {
371 			log.warn("Stopwatch not propery initialised! Call to getGroupReports() was ignored! Try to reset Stopwatch.");
372 			return new Report[0];
373 		}
374 		Report[] reports = engine.getStorage().getGroupReports(group);
375 		Arrays.sort(reports, comparator);
376 		return reports;
377 	}
378 
379 
380 	/***
381 	 * Generates an array of reports which contains exactly 1 element for each <b>label</b> If
382 	 * there is no enough data to produce the report, this method returns <code>null</code>
383 	 * 
384 	 * @param label the label for which report should be generated
385 	 * @return array of reports or <code>null</code>.
386 	 */
387 	public static Report[] getLabelReports(String label) {
388 		return getLabelReports(label, null);
389 	}
390 
391 
392 	/***
393 	 * Generates an array of reports which contains exactly 1 element for each <b>label</b> If
394 	 * there is no enough data to produce the report, this method returns <code>null</code>
395 	 * 
396 	 * @param label the label for which report should be generated
397 	 * @return array of reports or <code>null</code>.
398 	 */
399 	public static Report[] getLabelReports(String label, ReportComparator comparator) {
400 		if (!Stopwatch.initialised) {
401 			log.warn("Stopwatch not propery initialised! Call to getLabelReports() was ignored! Try to reset Stopwatch.");
402 			return new Report[0];
403 		}
404 
405 		Report[] reports = engine.getStorage().getLabelReports(label);
406 		Arrays.sort(reports, comparator);
407 		return reports;
408 	}
409 
410 
411 	/***
412 	 * Generates a single report for provided <b>group</b> and <b>label</b> If there is no enough
413 	 * data to produce the report, this method returns <code>null</code>
414 	 * 
415 	 * @param group the group for which report should be generated
416 	 * @param label the label for which report should be generated
417 	 * @return single report for provided <b>group</b> and <b>label</b> of <code>null</code>.
418 	 */
419 	public static Report getSingleReport(String group, String label) {
420 		if (!Stopwatch.initialised) {
421 			log.warn("Stopwatch not propery initialised! Call to getSingleReport() was ignored! Try to reset Stopwatch.");
422 			return null;
423 		}
424 		return engine.getStorage().getReport(group, label);
425 	}
426 
427 
428 	/***
429 	 * Returns information of how many instances of any measured code ware running for the last
430 	 * <code>numberOfPeriods</code> periods. Period length is defined by <code>periodField</code>
431 	 * which can be one of {@link java.util.Calendar#FIELD_NAME}
432 	 * <p>
433 	 * For example to see how many peaces of measured code were running per minute for the last 30
434 	 * minutes, one could use:
435 	 * 
436 	 * <pre>
437 	 * 		long[] load = Stopwatch.getLoad({@link java.util.Calendar#MINUTE}, 30);
438 	 * </pre>
439 	 * 
440 	 * In this case <code>load[0]</code> will contain the number of code instances running 30
441 	 * minutes ago and <code>load[29]</code> number of code instances running in the last minute.
442 	 * 
443 	 * @param periodField can be one of {@link java.util.Calendar#FIELD_NAME}
444 	 * @param numberOfPeriods number of periods
445 	 * @return array of length <code>numberOfPeriods</code> where every element represents the
446 	 *         load for given pariod.
447 	 */
448 	public static long[] getLoad(int periodField, int numberOfPeriods) {
449 		if (!Stopwatch.initialised) {
450 			log.warn("Stopwatch not propery initialised! Call to getLoad() was ignored! Try to reset Stopwatch.");
451 			return new long[0];
452 		}
453 		return engine.getStorage().getLoad(null, null, periodField, numberOfPeriods);
454 	}
455 
456 
457 	/***
458 	 * Returns information of how many instances of any measured code in group <code>group</code>
459 	 * ware running for the last <code>numberOfPeriods</code> periods. Period length is defined by
460 	 * <code>periodField</code> which can be one of {@link java.util.Calendar#FIELD_NAME}
461 	 * <p>
462 	 * For example to see how many peaces of measured code in group <code>g1</code> were running
463 	 * per day for the last 10 days, one could use:
464 	 * 
465 	 * <pre>
466 	 * 		long[] load = Stopwatch.getLoad({@link java.util.Calendar#DAY_OF_MONTH}, 10);
467 	 * </pre>
468 	 * 
469 	 * In this case <code>load[0]</code> will contain the number of code instances running 10 days
470 	 * ago and <code>load[9]</code> number of code instances running today.
471 	 * 
472 	 * @param group the group for which report should be generated
473 	 * @param periodField can be one of {@link java.util.Calendar#FIELD_NAME}
474 	 * @param numberOfPeriods number of periods
475 	 * @return array of length <code>numberOfPeriods</code> where every element represents the
476 	 *         load for given period.
477 	 */
478 	public static long[] getGroupLoad(String group, int periodField, int numberOfPeriods) {
479 		if (!Stopwatch.initialised) {
480 			log.warn("Stopwatch not propery initialised! Call to getGroupLoad() was ignored! Try to reset Stopwatch.");
481 			return new long[0];
482 		}
483 		return engine.getStorage().getLoad(group, null, periodField, numberOfPeriods);
484 	}
485 
486 
487 	/***
488 	 * Returns information of how many instances of any measured code labeled <code>label</code>
489 	 * ware running for the last <code>numberOfPeriods</code> periods. Period length is defined by
490 	 * <code>periodField</code> which can be one of {@link java.util.Calendar#FIELD_NAME}
491 	 * <p>
492 	 * For example to see how many peaces of measured code labeled <code>l1</code> were running
493 	 * per second for the last 15 seconds, one could use:
494 	 * 
495 	 * <pre>
496 	 * 		long[] load = Stopwatch.getLoad({@link java.util.Calendar#SECOND}, 15);
497 	 * </pre>
498 	 * 
499 	 * In this case <code>load[0]</code> will contain the number of code instances running 15
500 	 * seconds ago and <code>load[14]</code> number of code instances running in the last second.
501 	 * 
502 	 * @param label the label for which report should be generated
503 	 * @param periodField can be one of {@link java.util.Calendar#FIELD_NAME}
504 	 * @param numberOfPeriods number of periods
505 	 * @return array of length <code>numberOfPeriods</code> where every element represents the
506 	 *         load for given period.
507 	 */
508 	public static long[] getLabelLoad(String label, int periodField, int numberOfPeriods) {
509 		if (!Stopwatch.initialised) {
510 			log.warn("Stopwatch not propery initialised! Call to getLoad() was ignored! Try to reset Stopwatch.");
511 			return new long[0];
512 		}
513 		return engine.getStorage().getLoad(null, label, periodField, numberOfPeriods);
514 	}
515 
516 
517 	/***
518 	 * Returns information of how many instances of any measured code in group <code>group</code>
519 	 * labeled <code>label</code> ware running for the last <code>numberOfPeriods</code>
520 	 * periods. Period length is defined by <code>periodField</code> which can be one of
521 	 * {@link java.util.Calendar#FIELD_NAME}
522 	 * <p>
523 	 * For example to see how many peaces of measured code in group <code>g1</code> labeled
524 	 * <code>l1</code> were running per second for the last 3 weeks, one could use:
525 	 * 
526 	 * <pre>
527 	 * 		long[] load = Stopwatch.getLoad({@link java.util.Calendar#WEEK_OF_YEAR}, 3);
528 	 * </pre>
529 	 * 
530 	 * In this case <code>load[0]</code> will contain the number of code instances running 3 weeks
531 	 * ago and <code>load[2]</code> number of code instances running in the last week.
532 	 * 
533 	 * @param group the group for which report should be generated
534 	 * @param label the label for which report should be generated
535 	 * @param periodField can be one of {@link java.util.Calendar#FIELD_NAME}
536 	 * @param numberOfPeriods number of periods
537 	 * @return array of length <code>numberOfPeriods</code> where every element represents the
538 	 *         load for given period.
539 	 */
540 	public static long[] getLoad(String group, String label, int periodField, int numberOfPeriods) {
541 		if (!Stopwatch.initialised) {
542 			log.warn("Stopwatch not propery initialised! Call to getLoad() was ignored! Try to reset Stopwatch.");
543 			return new long[0];
544 		}
545 		return engine.getStorage().getLoad(group, label, periodField, numberOfPeriods);
546 	}
547 
548 
549 	/***
550 	 * 
551 	 * 
552 	 */
553 	public static void reset() {
554 		engine.stop();
555 		init();
556 	}
557 
558 
559 	/***
560 	 * Called to check if Stopwatch is active. When this method returns <code>false</code> all
561 	 * calls to {@link #start(String, String)} and {@link #stop(long)} are ignored.
562 	 * 
563 	 * @return Returns the Stopwatch's status.
564 	 */
565 	public static boolean isActive() {
566 		return active;
567 	}
568 
569 
570 	/***
571 	 * This method changes stopwatch's status Should be used to activate/deactivate Stopwatch at
572 	 * runtime.
573 	 * 
574 	 * @param active the Stopwatch's status.
575 	 */
576 	public static void setActive(boolean active) {
577 
578 		// skip if not changed
579 		if (Stopwatch.active == active) return;
580 
581 		if (!Stopwatch.initialised) {
582 			log.warn("Stopwatch not propery initialised! Call to setActive() was ignored! Try to reset Stopwatch.");
583 			return;
584 		}
585 
586 		// inform engine;
587 		if (active == false) {
588 			engine.pause();
589 		} else {
590 			engine.resume();
591 		}
592 
593 		Stopwatch.active = active;
594 	}
595 
596 
597 	/***
598 	 * Called to check if Stopwatch should produce debug information.
599 	 * 
600 	 * @see #setDebugEnabled(boolean)
601 	 * @return Returns the <code>true</code> or <code>false</code>
602 	 */
603 	public static boolean isDebugEnabled() {
604 		return debugEnabled;
605 	}
606 
607 
608 	/***
609 	 * Used to disable/enable Stopwatch's debug information. The reason for this method to exist is
610 	 * to be able to minimize the performance impact Stopwatch may have on the measured application.
611 	 * Generating debug info consumes additional CPU units, which may become a problem if Stopwatch
612 	 * is heavily used.
613 	 * 
614 	 * Setting this to false (is is false by default) will cause no debug info being generated by
615 	 * Stopwatch even when log4j's level is set to DEBUG.
616 	 * 
617 	 * @param debugEnabled should debug information be generated
618 	 */
619 	public static void setDebugEnabled(boolean debugEnabled) {
620 		Stopwatch.debugEnabled = debugEnabled;
621 	}
622 
623 
624 	/***
625 	 * @return true if debug is enabled
626 	 */
627 	private static boolean isDebug() {
628 		return isDebugEnabled() && log.isDebugEnabled();
629 	}
630 
631 
632 	/***
633 	 * Tries to get the value of property <b>key</b>
634 	 * 
635 	 * @param key
636 	 * @param defaultValue
637 	 * @return the value of property <b>key</b> of <code>defaultValue</code> if property not
638 	 *         found.
639 	 */
640 	public static String getProperty(String key, String defaultValue) {
641 		// first try properties from file
642 		String result = stopwatchProperties.getProperty(key, defaultValue);
643 		// then try system properties
644 		result = System.getProperty(SYSTEM_PROPERTIES_PREFIX + key, result);
645 
646 		if (result != null && result.trim().length() > 0) return result.trim();
647 		return defaultValue;
648 	}
649 
650 
651 	/***
652 	 * Loads properties from given location. Returns an empty instance of {@link Properties} if for
653 	 * any reason properties can not be read!
654 	 * 
655 	 * @param location the URI to load properties from
656 	 * @return the actual properties or an empty instance of {@link Properties} on error
657 	 */
658 	private static Properties readPropertiesFromLocation(String location) {
659 		Properties result = new Properties();
660 
661 		if (location == null) {
662 			if (isDebug()) {
663 				log.debug("Missing properties location !");
664 			}
665 			return result;
666 		}
667 
668 		URI uri;
669 		try {
670 			uri = new URI(location);
671 		} catch (URISyntaxException e) {
672 			log.warn("Invalid properties location " + location + "! Properties not set!", e);
673 			return result;
674 		}
675 
676 		InputStream propertiesStream;
677 		if ("classpath".equalsIgnoreCase(uri.getScheme())) {
678 			propertiesStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(uri.getSchemeSpecificPart());
679 			if (propertiesStream == null) {
680 				log.warn("Properties file " + uri.getSchemeSpecificPart() + " not found on classpath! Properties not set!");
681 				return result;
682 			}
683 		} else if ("file".equalsIgnoreCase(uri.getScheme())) {
684 			try {
685 				propertiesStream = new FileInputStream(new File(uri.getSchemeSpecificPart()));
686 			} catch (FileNotFoundException e) {
687 				log.warn("Properties file " + uri.getSchemeSpecificPart() + " not found! Properties not set!");
688 				return result;
689 			}
690 		} else {
691 			log.warn("Unknown schema " + uri.getScheme() + " in properties location! Properties not set!");
692 			return result;
693 		}
694 
695 		try {
696 			result.load(propertiesStream);
697 		} catch (IOException e) {
698 			log.warn("Error while loading properties! Properties not set!", e);
699 		} finally {
700 			try {
701 				propertiesStream.close();
702 			} catch (IOException e) {
703 				log.warn("Error while loading properties!", e);
704 			}
705 		}
706 		return result;
707 	}
708 
709 
710 	/***
711 	 * 
712 	 */
713 	private static void init() {
714 		initialised = true;
715 		InputStream propertiesInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("stopwatch.properties");
716 		if (propertiesInputStream != null) {
717 			try {
718 				stopwatchProperties.load(propertiesInputStream);
719 			} catch (IOException e) {
720 				log.warn("Problem loading 'stopwatch.properties' file!", e);
721 			}
722 		}
723 
724 		active = Boolean.valueOf(getProperty(PROPERTY_ACTIVE, Boolean.toString(false))).booleanValue();
725 		debugEnabled = Boolean.valueOf(getProperty(PROPERTY_DEBUG, Boolean.toString(false))).booleanValue();
726 		jmxManaged = Boolean.valueOf(getProperty(PROPERTY_JMX_MANAGED, Boolean.toString(false))).booleanValue();
727 		mBeanServerName = getProperty(PROPERTY_MBEAN_SERVER_NAME, null);
728 		String engineClass = getProperty(PROPERTY_ENGINE, DEFAULT_ENGINE);
729 		String enginePropertiesLocation = getProperty(PROPERTY_ENGINE_PROPERTIES, DEFAULT_ENGINE_PROPERTIES);
730 		String storageClass = getProperty(PROPERTY_STORAGE, null);
731 		String storagePropertiesLocation = getProperty(PROPERTY_STORAGE_PROPERTIES, DEFAULT_STORAGE_PROPERTIES);
732 		persistenceMode = getProperty(PROPERTY_MODE, null);
733 		try {
734 			engine = (StopwatchEngine) Class.forName(engineClass).newInstance();
735 			engine.setDebugEnabled(debugEnabled);
736 			engine.setProperties(readPropertiesFromLocation(enginePropertiesLocation));
737 
738 			// if storage is configured then pass it to the engine
739 			if (storageClass != null && storageClass.trim().length() > 0) {
740 				StopwatchStorage storage = (StopwatchStorage) Class.forName(storageClass).newInstance();
741 				storage.setDebugEnabled(debugEnabled);
742 				storage.setProperties(readPropertiesFromLocation(storagePropertiesLocation));
743 				engine.setStorage(storage);
744 			}
745 
746 			// if persistence mode is configured then pass it to the engine
747 			if ("NORMAL".equals(persistenceMode)) {
748 				engine.setPersistenceMode(StorageManager.NORMAL_MODE);
749 			} else if ("THREAD".equals(persistenceMode)) {
750 				engine.setPersistenceMode(StorageManager.THREAD_MODE);
751 			} else if ("DELAYED".equals(persistenceMode)) {
752 				engine.setPersistenceMode(StorageManager.DELAYED_MODE);
753 			} else {
754 				if (persistenceMode != null) log.warn("Unknown persistence mode: " + persistenceMode + "! Using default !");
755 				engine.setPersistenceMode(DEFAULT_MODE);
756 				persistenceMode = "DELAYED";
757 			}
758 
759 			engine.start();
760 
761 			if (jmxManaged) {
762 				agent = new StopwatchAgent(mBeanServerName);
763 				agent.start();
764 			}
765 
766 		} catch (InstantiationException e) {
767 			active = false;
768 			initialised = false;
769 			log.warn("Stopwatch was deactivated because an error(s) occurred during initialization!", e);
770 		} catch (IllegalAccessException e) {
771 			active = false;
772 			initialised = false;
773 			log.warn("Stopwatch was deactivated because an error(s) occurred during initialization!", e);
774 		} catch (ClassNotFoundException e) {
775 			active = false;
776 			initialised = false;
777 			log.warn("Stopwatch was deactivated because an error(s) occurred during initialization!", e);
778 		}
779 	}
780 
781 
782 	public static String getEngineClass() {
783 		return engine.getClass().getName();
784 	}
785 
786 
787 	public static String getStorageClass() {
788 		return engine.getStorageClass();
789 	}
790 
791 
792 	public static String getPersistenceMode() {
793 		return persistenceMode;
794 	}
795 
796 
797 	/***
798 	 * @return the initialised
799 	 */
800 	public static boolean isInitialised() {
801 		return initialised;
802 	}
803 
804 }