001: package com.jamonapi;
002:
003: import java.util.*;
004: import java.text.*;
005: import com.jamonapi.utils.*;
006:
007: /** MonitorComposite can contain other MonitorComposite and TimingMonitors (at the leaf level) **/
008:
009: public class MonitorComposite implements MinimalMonitor, CompositeNode,
010: MonitorReportInterface {
011: /** Returns the accrued value of all Monitors underneath this MonitorComposite. Note this method will recursively call
012: * getAccrued() for any nested MonitorComposites. Note this method uses the Gang of 4's Command pattern.
013: **/
014: public long getAccrued() {
015: // sums up the total of all TotalMonitor() objects within the TimingMonitor. Note this means that
016: // the TimingMonitor() must be constructed like new TimingMonitor(new TotalMonitor(...))
017: class AccruedCommand implements Command {
018: long accrued;
019:
020: public void execute(Object monitor) throws Exception {
021: if (monitor instanceof TimingMonitor)
022: accrued += (((TimingMonitor) monitor).childMonitor)
023: .getAccrued();
024: else
025: accrued += ((MonitorComposite) monitor)
026: .getAccrued();
027: }
028: }
029:
030: AccruedCommand command = new AccruedCommand();
031: iterate(command);
032:
033: return command.accrued;
034: }
035:
036: /** Display the accrued value as a string **/
037: public String toString() {
038: return getAccruedString();
039: }
040:
041: /** Call reset() on all Monitors that this MonitorComposite instance contains **/
042: public void reset() {
043: class ResetCommand implements Command {
044: public void execute(Object monitor) throws Exception {
045: ((MinimalMonitor) monitor).reset();
046: }
047: }
048:
049: iterate(new ResetCommand());
050: }
051:
052: /** Iterate through the MonitorComposite executing the passed in Command object. Note This is an example of the Gang of 4's
053: * Command and Iterator patterns
054: **/
055: void iterate(Command command) {
056: try {
057: CommandIterator.iterate(monitorList.values().iterator(),
058: command);
059: } catch (Exception e) {
060: throw AppBaseException.getRuntimeException(e);
061: }
062: }
063:
064: void iterateMapEntries(Command command) {
065: try {
066: CommandIterator.iterate(monitorList, command);
067: } catch (Exception e) {
068: throw AppBaseException.getRuntimeException(e);
069: }
070: }
071:
072: /** Call increase(long) on all Monitors that this MonitorComposite instance contains **/
073: public void increase(final long increaseValue) {
074:
075: class IncreaseCommand implements Command {
076: public void execute(Object monitor) throws Exception {
077: ((MinimalMonitor) monitor).increase(increaseValue);
078: }
079: }
080:
081: iterate(new IncreaseCommand());
082: }
083:
084: // the next method really shouldn't need to be called. i put it in to honor the interface. it should probably
085: // be refactored out
086: public void getData(ArrayList rowData) {
087: }
088:
089: // shouldn't be called. refactor this out.
090: public void getHeader(ArrayList header) {
091: }
092:
093: /** Return the contents of this MonitorComposite as an HTML table. This method is called to display the data in a MonitorComposite
094: * in a servlet or JSP. See www.jamonapi.com for a sample of what the report shoud look like.
095: *
096: * Sample Call:
097: * String html=monitorComposite.getReport();
098: **/
099: public String getReport() throws Exception {
100: MonitorConverter mc = new MonitorConverter(this );
101: return mc.getReport();
102: }
103:
104: /** Return the contents of this MonitorComposite as an HTML table sorted by the specified column number in ascending or descending order.
105: * This method is called to display the data in a MonitorComposite in a servlet or JSP. See www.jamonapi.com for a sample of what
106: * the report shoud look like.
107: *
108: * Sample Call:
109: * String html=monitorComposite.getReport(2, "asc"); // return the html report sorted by column 2 in ascending order
110: **/
111: public String getReport(int sortCol, String sortOrder)
112: throws Exception {
113: MonitorConverter mc = new MonitorConverter(this );
114: return mc.getReport(sortCol, sortOrder);
115:
116: }
117:
118: /** Return the contents of this MonitorComposite as a 2 dimensional array of Strings. This is the data minus the formatting of
119: * what is returned by the getReport() method.
120: **/
121: public String[][] getData() {
122: MonitorConverter mc = new MonitorConverter(this );
123: return mc.getData();
124:
125: }
126:
127: /** Return the contents of this MonitorComposite as a 2 dimensional array of Strings. The passed "label" will be prepended to the
128: * first column in the array.
129: * Sample Call:
130: * String[][] arr=monitorComposite.getData("myLabel");
131: **/
132: public String[][] getData(String label) {
133: MonitorConverter mc = new MonitorConverter(this );
134: return mc.getData(label);
135:
136: }
137:
138: private static String displayDelimiter = ".";
139:
140: /** Delimiter to be used when displaying the monitor label returned by the getReport() and getData() methods. The default is a period.
141: * A better approach would have been to simply display the delimeter that was used on the MonitorComposites creation
142: **/
143: public static void setDisplayDelimiter(String localDisplayDelimiter) {
144: displayDelimiter = localDisplayDelimiter;
145: }
146:
147: /** This is CompositeNode interface method that returns the child MonitorComposite identified by the given label or it creates a
148: * new CompositeMonitor. This method is used by the factory classes to create MonitorComposites.
149: *
150: * Sample Call:
151: * monitorComposite.getCompositeNode("myApp.jsp")
152: **/
153: public CompositeNode getCompositeNode(String childNodeName) {
154: if (compositeNodeExists(childNodeName))
155: return getExistingCompositeNode(childNodeName);
156: else
157: return new MonitorComposite(); // Use MonitorFactory or is it not needed anymore?
158: }
159:
160: /** This is CompositeNode interface method that returns a leaf node identified by the given label or it creates a
161: * new leaf node by calling the Leaf. This method is used by the factory classes to create TimingMonitors.
162: *
163: * Sample Call:
164: * monitorComposite.getCompositeNode("myApp.jsp");
165: **/
166: public LeafNode getLeafNode(String childNodeName,
167: String childNodeType) {
168: if (leafNodeExists(childNodeName)) {
169: TimingMonitor mon = (TimingMonitor) getExistingLeafNode(childNodeName);
170: return mon;
171: } else
172: return leafFactory.createInstance(childNodeType);
173: }
174:
175: /** Add a CompositeNode to the data structure if it doesn't exist. **/
176: public void addCompositeNode(String childNodeName,
177: CompositeNode addNode) {
178:
179: boolean nodeExists = compositeNodeExists(childNodeName);
180:
181: if (!nodeExists)
182: monitorList
183: .put(getCompositeNodeKey(childNodeName), addNode);
184: }
185:
186: /** Does the passed in leaf node exist. Returns true if there has been a LeafNode created with this label.
187: *
188: * Sample Call:
189: * monitorComposite.leafNodeExists("myApp.jsp.homePage");
190: **/
191: public boolean leafNodeExists(String childNodeName) {
192: return monitorList.containsKey(getLeafNodeKey(childNodeName));
193:
194: }
195:
196: /** Does the passed in CompositeNode exist. Returns true if there has been a CompositeNode created with this label.
197: *
198: * Sample Call:
199: * monitorComposite.compositeNodeExists("myApp.jsp");
200: **/
201: public boolean compositeNodeExists(String childNodeName) {
202: return monitorList
203: .containsKey(getCompositeNodeKey(childNodeName));
204:
205: }
206:
207: /** Returns the CompositeNode if it exists.
208: *
209: * Sample Call:
210: * monitorComposite.compositeNodeExists("myApp.jsp");
211: **/
212: public CompositeNode getExistingCompositeNode(String childNodeName) {
213: return (CompositeNode) monitorList
214: .get(getCompositeNodeKey(childNodeName));
215: }
216:
217: /** Returns the LeafNode if it exists.
218: *
219: * Sample Call:
220: * monitorComposite.compositeNodeExists("myApp.jsp");
221: **/
222: public LeafNode getExistingLeafNode(String childNodeName) {
223: return (LeafNode) monitorList
224: .get(getLeafNodeKey(childNodeName));
225: }
226:
227: /** Add a LeafNode to the data structure if it doesn't exist. **/
228: public void addLeafNode(String childNodeName, LeafNode addNode) {
229: boolean nodeExists = leafNodeExists(childNodeName);
230:
231: if (!nodeExists)
232: monitorList.put(getLeafNodeKey(childNodeName), addNode);
233: }
234:
235: /** Return this MonitorComposite as a CompositeNode **/
236: public CompositeNode getRootNode() {
237: return this ;
238: }
239:
240: /** This reverses getCompositeNodeKey() and getLeafNodeKey() **/
241: protected String getLabelFromKey(String key) { // if the location that we are after is pages.homepage then in the hashmap the values look like
242: // pagesChomepageL where pagesC means it is a composite and homepageL means it is a leaf node.
243: // The logic below gets rid of the C and L and puts a period at the end of a composite. i.e.
244: // pagesChomepageL becomes pages.homepage
245:
246: // if key is "pagesC" return "pages.". if key is homepageL return "homepage"
247: int lastCharPos = key.length() - 1;
248: String lastChar = "";
249:
250: if ('C' == key.charAt(lastCharPos))
251: lastChar = displayDelimiter;
252:
253: return key.substring(0, lastCharPos) + lastChar;
254: }
255:
256: /**
257: * getCompositeNodeKey(...) and getLeafNodeKey(...) are used to ensure that composite nodes are not replaced by leaf nodes
258: * and vice versa. For example the following 2 entries could both be simultaneously valid even though their labels are
259: * identical.
260: *
261: * Leaf Node: pages.homePage
262: * Composite Node: pages.homePage
263: *
264: * This is because leafs and composites have different keys behind the scenes. Using the example above the
265: * keys may look something like:
266: *
267: * Leaf Node Key: pagesC.homePageL
268: * Composite Node: pagesC.homePageC
269: *
270: */
271: public String getCompositeNodeKey(String nodeName) {
272: final String COMPOSITE_NODE_KEY_APPENDER = "C";
273: return nodeName + COMPOSITE_NODE_KEY_APPENDER;
274: }
275:
276: /**
277: * getCompositeNodeKey(...) and getLeafNodeKey(...) are used to ensure that composite nodes are not replaced by leaf nodes
278: * and vice versa. For example the following 2 entries could both be simultaneously valid even though their labels are
279: * identical.
280: *
281: * Leaf Node: pages.homePage
282: * Composite Node: pages.homePage
283: *
284: * This is because leafs and composites have different keys behind the scenes. Using the example above the
285: * keys may look something like:
286: *
287: * Leaf Node Key: pagesC.homePageL
288: * Composite Node: pagesC.homePageC
289: *
290: */
291: public String getLeafNodeKey(String nodeName) {
292: final String LEAF_NODE_KEY_APPENDER = "L";
293: return nodeName + LEAF_NODE_KEY_APPENDER;
294: }
295:
296: /** Return the accrued value in String format **/
297: public String getAccruedString() {
298: DecimalFormat numberFormat = (DecimalFormat) NumberFormat
299: .getNumberInstance();
300: numberFormat.applyPattern("#,###");
301: return numberFormat.format(getAccrued());
302: }
303:
304: /** Return an array that contains the Header to be used in reports **/
305: protected static String[] getHeader() {
306: return leafFactory.getHeader();
307: }
308:
309: private Map monitorList = new TreeMap();
310: private static MonitorLeafFactory leafFactory = new MonitorLeafFactory();
311: }
|