001: package com.jamonapi;
002:
003: import java.util.*;
004: import com.jamonapi.utils.*;
005:
006: /**
007: * MonitorFactory - Class that controls monitors including creating, starting, enabling, disabling and resetting them.
008: * This class uses the Gang of 4's creational patterns. Monitors returned by MonitorFactory are thread safe.
009: *
010: * @author Steve Souza
011: * @version 1.01
012: */
013: public class MonitorFactory extends java.lang.Object {
014: private static MonitorFactoryEnabled enabledFactory = new MonitorFactoryEnabled();
015: private static MonitorFactoryDisabled disabledFactory = new MonitorFactoryDisabled();
016:
017: private static boolean debugEnabled = true;
018: private static boolean enabled = true;
019: private static int debugPriorityLevel = 0;
020:
021: /** Returns the composite monitor specified in the argument. Composite Monitor's are groups of other monitors
022: * (including other composites). <br>
023: *
024: * <p><code>Sample Call: String html=MonitorFactory.getComposite("pages").getReport(); </code></p>
025: *
026: * @param locator A string that locates the Composite Monitor.
027: * @return MonitorComposite
028: */
029: public static MonitorComposite getComposite(String locator) {
030: return getFactory().getComposite(locator);
031: }
032:
033: /** Returns the factory for creating debug monitors. The debug factory can be disabled independently from the
034: * regular factory. Debug monitors are no different than monitors returned by the regular monitor factory.
035: * However the debug factory can be used to monitor items in a test environment and disable them in production.<br>
036: *
037: * <p><code>Sample Call: MonitorFactory.getDebugFactory().start();</code></p>
038: *
039: * @return MonitorFactoryInterface
040: */
041: public static MonitorFactoryInterface getDebugFactory() {
042: return getFactory(isDebugEnabled());
043: }
044:
045: /**
046: * Returns the factory for creating debug monitors. The debug factory can be disabled independently from the
047: * regular factory. Debug monitors are no different than monitors returned by the regular monitor factory.
048: * However the debug factory can be used to monitor items in a test environment and disable them in production.<br>
049: * This method takes a priority level threshold as an argument. If the passed priority level is <b>greater than or equal</b> to the
050: * MonitorFactory debug priority level then a Monitor is returned or else a null Monitor is returned (no statistics will be kept).<br>
051: *
052: * <p><code>Sample Call: MonitorFactory.getDebugFactory(20).start();<br>
053: * if MonitorFactory debug priority level is 10 then a non null Monitor will be returned</code></p>
054: *
055: * @return MonitorFactoryInterface
056: */
057: public static MonitorFactoryInterface getDebugFactory(
058: int _debugPriorityLevel) {
059: return getFactory(isDebugEnabled(_debugPriorityLevel));
060: }
061:
062: /**
063: * Returns all gathered statistics as an HTML table as a String. This can be displayed in a JSP or Servlet.<br>
064: *
065: * <p><code>Sample Call: String html=MonitorFactory.getReport();</code></p>
066: *
067: **/
068: public static String getReport() throws Exception {
069: return getRootMonitor().getReport();
070: }
071:
072: /**
073: * Returns gathered statistics underneath lower in the heirarchy than the locator string.
074: * The returned String is as an HTML table as a String. This can be displayed in a JSP or Servlet.<br>
075: *
076: * <p><code>Sample Call: String html=MonitorFactory.getReport("MyApplication.DataAccess");<br>
077: * This would return statistics for MyApplication.DataAccess.open(), MyApplication.DataAccess.close(),...</code></p>
078: *
079: **/
080: public static String getReport(String locator) throws Exception {
081: return getComposite(locator).getReport();
082: }
083:
084: /** Returns the topmost Composite Monitor **/
085: public static MonitorComposite getRootMonitor() {
086: return getFactory().getRootMonitor();
087: }
088:
089: /**
090: * Wipes out all statistics that have been gathered. Subsequent calls to the start(...) methods will continue to gather statistics.<br>
091: *
092: * <p><code>Sample Call: MonitorFactory.reset();</code></p>
093: **/
094: public static void reset() {
095: enabledFactory = new MonitorFactoryEnabled();
096: }
097:
098: /**
099: * Enable or disable the debug factory. The debug factory can be enabled/disabled at runtime. Calling this method with a false
100: * also disables calls to MonitorFactory.getDebugFactory(int debugPriorityLevel)<br>
101: *
102: * <p><code>Sample Call:<br>
103: * MonitorFactory.setDebugEnabled(false);<br>
104: * MonitorFactory.getDebugFactory().start(); // no stats are gathered.<br>
105: * MonitorFactory.getDebugFactory(20).start(); // no stats are gathered.<br></code></p>
106: */
107: public static void setDebugEnabled(boolean _debugEnabled) {
108: debugEnabled = _debugEnabled;
109: }
110:
111: /**
112: * Enable or disable the priority driven debug factory. The debug factory can be enabled/disabled at runtime.
113: * Calling this method with a false has no effect on MonitorFactory.getDebugFactory()<br>
114: *
115: * <p><code>Sample Call:<br>
116: * MonitorFactory.setDebugEnabled(false);<br>
117: * MonitorFactory.getDebugFactory(20).start(); // no stats are gathered.<br></code></p>
118: */
119: public static void setDebugPriorityLevel(int _debugPriorityLevel) {
120: debugPriorityLevel = _debugPriorityLevel;
121: }
122:
123: /**
124: * Enable or disable the factory. The factory can be enabled/disabled at runtime. Calling this method with a false
125: * also disables calls to both MonitorFactory.getDebugFactory(), and MonitorFactory.getDebugFactory(int debugPriorityLevel)<br>
126: *
127: * <p><code>Sample Call:<br>
128: * MonitorFactory.setEnabled(false);<br>
129: * MonitorFactory.start(); // no stats are gathered.<br>
130: * MonitorFactory.start("MyApp.DataAccess.open()"); // no stats are gathered.<br>
131: * MonitorFactory.getDebugFactory().start(); // no stats are gathered.<br>
132: * MonitorFactory.getDebugFactory(20).start(); // no stats are gathered.<br></code></p>
133: */
134: public static void setEnabled(boolean _enabled) {
135: enabled = _enabled;
136: }
137:
138: /** Call this method if you don't want to use the default name or location for JAMonAdmin.jsp that is returned in the JAMon report.
139: **/
140: public static void setJAMonAdminPage(String JAMonAdminPage) {
141: MonitorConverter.setJAMonAdminPage(JAMonAdminPage);
142: }
143:
144: /** Return a Monitor and begin gathering timing statistics for it.<br>
145: *
146: * <p><code>Sample Call:<br>
147: * Monitor mon=MonitorFactory.start("MyApp.DataAccess.open()");<br>
148: * ...code being monitored...<br>
149: * System.out.println(mon.stop());<br></code></p>
150: **/
151: public static Monitor start(String locator) {
152: return getFactory().start(locator);
153: }
154:
155: /** Return a Monitor and begin gathering timing statistics for it. Statistics for this start() method will not be returned by
156: * MonitorFactory.getReport()<br>
157: *
158: * <p><code>Sample Call:<br>
159: * Monitor mon=MonitorFactory.start();<br>
160: * ...code being monitored...<br>
161: * System.out.println(mon.stop());<br></code></p>
162: **/
163: public static Monitor start() {
164: return getFactory().start();
165: }
166:
167: /** Return a Monitor and begin gathering timing statistics for it. See the online JAMon users manual for an explanation of this
168: * method.<br>
169: *
170: * <p><code>Sample Call:<br>
171: * Monitor mon=MonitorFactory.startPrimary("MyApp.jsp.HomePage");<br>
172: * ...code being monitored...<br>
173: * System.out.println(mon.stop());<br></code></p>
174: **/
175: public static Monitor startPrimary(String locator) {
176: return getFactory().startPrimary(locator);
177: }
178:
179: //******* Protected methods
180: protected static MonitorFactoryInterface getFactory() {
181: return getFactory(isEnabled());
182: }
183:
184: protected static MonitorFactoryInterface getFactory(boolean _enabled) {
185: if (_enabled)
186: return enabledFactory;
187: else
188: return disabledFactory;
189: }
190:
191: // Note for debug to be considered enabled the factory must also be enabled.
192: protected static boolean isDebugEnabled() {
193: return debugEnabled && isEnabled();
194: }
195:
196: protected static boolean isDebugEnabled(int _debugPriorityLevel) {
197: // for a given debug level to be enabled all of the following must be true
198: // - the factory must be enabled.
199: // - debugging must be enabled
200: // - the priority level requested must be withing the acceptable range
201: return _debugPriorityLevel >= debugPriorityLevel
202: && isDebugEnabled();
203:
204: }
205:
206: /**
207: * Returns the state of the enabled flag.
208: *
209: * <p><code>Sample Call:<br>
210: * if (MonitorFactory.isEnabled())<br>
211: * ........// Do something which only makes sence when monitoring is enabled<br>
212: * else<br>
213: * ........// Do something which only makes sence when monitoring is disabled<br>
214: */
215: public static boolean isEnabled() {
216: return enabled;
217: }
218:
219: protected static void setNodeTree(NodeTree monitorTree) {
220: enabledFactory.setNodeTree(monitorTree);
221: }
222:
223: /*
224: Inner classes used for enabled and disabled factories. They both inherit from MonitorFactoryBase.
225: The classes are inner because they are an implementation detail for this class.
226: */
227: static abstract class MonitorFactoryBase implements
228: MonitorFactoryInterface {
229: protected MonitorComposite rootMonitor; // Parent to all other MonitorComposites
230:
231: public Monitor start(String locator) {
232: return createInstance(locator, MonitorLeafFactory.DEFAULT)
233: .start();
234: }
235:
236: public Monitor startPrimary(String locator) {
237: return createInstance(locator, MonitorLeafFactory.PRIMARY)
238: .start();
239: }
240:
241: abstract protected Monitor createInstance(String locator,
242: String type);
243:
244: }
245:
246: /* Factory used when a factory is enabled */
247: static class MonitorFactoryEnabled extends MonitorFactoryBase {
248:
249: // monitorTree is used to create the summary monitors the first time they are executed. After the monitors creation
250: // existingLeafNodes is used instead.
251: private NodeTree monitorTree;
252: //??? may need to synchronize iteration seperately.
253: private Map existingLeafNodes = Collections
254: .synchronizedMap(AppMap.createInstance());
255: private TimingMonitor simpleMonitor = new TimingMonitor(); // used for monitors that don't save summary statistics
256:
257: protected MonitorFactoryEnabled() {
258: rootMonitor = new MonitorComposite();
259: monitorTree = new NodeTree(rootMonitor);
260: }
261:
262: public void setNodeTree(NodeTree monitorTree) {
263: this .monitorTree = monitorTree;
264: }
265:
266: private boolean monitorExists(String locator) {
267: return existingLeafNodes.containsKey(locator);
268: }
269:
270: private Monitor getMonitor(String locator) {
271: // a new TimingMonitor is given to the requestor, however the same childMonitors are used.
272: // This allows for the accumulation of statistics.
273: Monitor mon = (TimingMonitor) existingLeafNodes
274: .get(locator);
275: return mon;
276:
277: }
278:
279: private Monitor createMonitor(String locator, String type) {
280: Monitor mon;
281:
282: synchronized (this ) {
283: // gets the Monitor from node tree map if it exists or creates the Monitor if it doesn't exist
284: mon = (TimingMonitor) monitorTree.getLeafNode(locator,
285: type);
286: existingLeafNodes.put(locator, mon);
287: }
288:
289: return mon;
290: }
291:
292: // If the monitor already exists get it from the existingLeafNodes, else create it via the slower NodeTree
293: protected Monitor createInstance(String locator, String type) {
294: if (monitorExists(locator))
295: return getMonitor(locator);
296: else
297: return createMonitor(locator, type);
298: }
299:
300: public Monitor start() {
301: return simpleMonitor.start();
302: }
303:
304: public MonitorComposite getComposite(String locator) {
305: // it is a programming error to get a nonexisting composite. composites are created when
306: // when of the creation methods are called (start(...) and createInstance())
307: if (!monitorTree.compositeNodeExists(locator))
308: throw new RuntimeException(
309: "The requested MonitorComposite does not exist: "
310: + locator);
311:
312: return (MonitorComposite) monitorTree
313: .getCompositeNode(locator);
314: }
315:
316: public MonitorComposite getRootMonitor() {
317: return rootMonitor;
318: }
319:
320: }
321:
322: // When a MonitorFactory is disabled it simply returns a NullMonitor (see Martin Fowler's excellent refactoring book)
323: // A null object is an object that provides the required interface, but no implementation. So when Monitor's are disabled
324: // the code still executes however it completes very quickly because the code doesn't do anything.
325: static class MonitorFactoryDisabled extends MonitorFactoryBase {
326:
327: private Monitor nullMonitor = new NullMonitor();
328:
329: protected Monitor createInstance(String locatory, String type) {
330: return nullMonitor;
331: }
332:
333: public Monitor start() {
334: return nullMonitor;
335: }
336:
337: public MonitorComposite getComposite(String locator) {
338: return new MonitorComposite();
339: }
340:
341: public MonitorComposite getRootMonitor() {
342: return new MonitorComposite();
343: }
344:
345: }
346:
347: /** Test code for the MonitorFactory class.
348: **/
349: public static void main(String[] args) throws Exception {
350:
351: Monitor testMon = null;
352:
353: for (int i = 0; i < 50; i++) {
354: testMon = MonitorFactory.start("pages/homepage");
355: Thread.sleep(10);
356: testMon.stop();
357: Thread.sleep(10);
358:
359: }
360:
361: System.out
362: .println("\nComposite test1 should be 10 ms., total 500 ms., 50 hits: "
363: + testMon.stop());
364:
365: for (int i = 0; i < 50; i++) {
366: testMon = MonitorFactory.start("pages.homepage");
367: Thread.sleep(10);
368: testMon.stop();
369: Thread.sleep(10);
370: }
371:
372: System.out
373: .println("\nComposite test2 (Should be double the previous numbers): "
374: + testMon.stop());
375: System.out.println("\ngetting root monitor: "
376: + getRootMonitor());
377:
378: }
379: }
|