001: /*
002: * $Id: FunctionStatistics.java,v 1.22 2007/09/18 08:45:05 agoubard Exp $
003: *
004: * Copyright 2003-2007 Orange Nederland Breedband B.V.
005: * See the COPYRIGHT file for redistribution and use restrictions.
006: */
007: package org.xins.server;
008:
009: import java.util.Iterator;
010: import java.util.TimeZone;
011: import java.util.TreeMap;
012:
013: import org.xins.common.text.DateConverter;
014: import org.xins.common.xml.Element;
015: import org.xins.common.xml.ElementBuilder;
016:
017: /**
018: * Statistics of a function.
019: *
020: * <p>The implementation of this class is thread-safe.
021: *
022: * @version $Revision: 1.22 $ $Date: 2007/09/18 08:45:05 $
023: * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
024: * @author <a href="mailto:anthony.goubard@japplis.com">Anthony Goubard</a>
025: *
026: * @since XINS 1.0.0
027: */
028: class FunctionStatistics {
029:
030: /**
031: * String to insert instead of a figure when the figure is unavailable.
032: */
033: private static final String NOT_AVAILABLE = "N/A";
034:
035: /**
036: * The time zone used when generating dates for output.
037: */
038: private static final TimeZone TIME_ZONE = TimeZone.getDefault();
039:
040: /**
041: * Constructs a new <code>FunctionStatistics</code> instance.
042: */
043: FunctionStatistics() {
044: _successful = new Statistic();
045: _unsuccessful = new Statistic();
046: _errorCodeStatistics = new TreeMap();
047: }
048:
049: /**
050: * Statistics for the successful calls. Never <code>null</code>.
051: */
052: private final Statistic _successful;
053:
054: /**
055: * Statistic over the unsuccessful calls. Never <code>null</code>.
056: */
057: private final Statistic _unsuccessful;
058:
059: /**
060: * Statistics over the unsuccessful calls sorted by error code.
061: * The key of the map is the error code and the Statistic object
062: * corresponding to the error code. Never <code>null</code>.
063: */
064: private final TreeMap _errorCodeStatistics;
065:
066: /**
067: * Callback method that may be called after a call to this function. This
068: * method will store statistics-related information.
069: *
070: * <p />This method does not <em>have</em> to be called. If statistics
071: * gathering is disabled, then this method should not be called.
072: *
073: * @param start
074: * the start time, in milliseconds since the UNIX Epoch.
075: *
076: * @param success
077: * indication if the call was successful.
078: *
079: * @param errorCode
080: * the error code returned by the function if a result is unsuccessful;
081: * this value is <code>null</code> only when <code>success</code>
082: * is <code>true</code>.
083: *
084: * @return
085: * returns the duration in milliseconds of the call of the function.
086: * The duration is computed as the difference in between
087: * the start time and the time that this method has been invoked.
088: */
089: final synchronized long recordCall(long start, boolean success,
090: String errorCode) {
091:
092: long duration = System.currentTimeMillis() - start;
093:
094: // Call succeeded
095: if (success) {
096:
097: _successful.recordCall(start, duration);
098:
099: // Call failed
100: } else {
101:
102: _unsuccessful.recordCall(start, duration);
103:
104: Statistic errorCodeStat = (Statistic) _errorCodeStatistics
105: .get(errorCode);
106: if (errorCodeStat == null) {
107: errorCodeStat = new Statistic();
108: }
109: errorCodeStat.recordCall(start, duration);
110: _errorCodeStatistics.put(errorCode, errorCodeStat);
111: }
112: return duration;
113: }
114:
115: /**
116: * Resets the statistics for this function.
117: */
118: final synchronized void resetStatistics() {
119: _successful.reset();
120: _unsuccessful.reset();
121: _errorCodeStatistics.clear();
122: }
123:
124: /**
125: * Get the successful statistic as an {@link org.xins.common.xml.Element}.
126: *
127: * @return
128: * the successful element, cannot be <code>null</code>
129: */
130: public synchronized Element getSuccessfulElement() {
131: return _successful.getElement(true, null);
132: }
133:
134: /**
135: * Get the unsuccessful statistics as an array of {@link org.xins.common.xml.Element}.
136: *
137: * @param detailed
138: * If <code>true</code>, the unsuccessful results will be returned
139: * per error code. Otherwise only one unsuccessful containing all
140: * unsuccessful result will be returned.
141: *
142: * @return
143: * the successful element, cannot be empty.
144: */
145: public synchronized Element[] getUnsuccessfulElement(
146: boolean detailed) {
147: if (!detailed || _errorCodeStatistics.size() == 0) {
148: Element[] result = new Element[1];
149: result[0] = _unsuccessful.getElement(false, null);
150: return result;
151: } else {
152: Element[] result = new Element[_errorCodeStatistics.size()];
153: int i = 0;
154: Iterator itErrorCodeStats = _errorCodeStatistics.keySet()
155: .iterator();
156: while (itErrorCodeStats.hasNext()) {
157: String nextErrorCode = (String) itErrorCodeStats.next();
158: Statistic nextStat = (Statistic) _errorCodeStatistics
159: .get(nextErrorCode);
160: result[i] = nextStat.getElement(false, nextErrorCode);
161: i++;
162: }
163: return result;
164: }
165: }
166:
167: /**
168: * Group of statistics data.
169: *
170: * <p>The implementation of this class is thread-safe.
171: *
172: * @author <a href="mailto:anthony.goubard@japplis.com">Anthony Goubard</a>
173: *
174: * @since XINS 1.1.0
175: */
176: private static final class Statistic {
177:
178: /**
179: * The number of successful calls executed up until now. Initially
180: * <code>0L</code>.
181: */
182: private int _calls;
183:
184: /**
185: * The start time of the most recent call. Initially <code>0L</code>.
186: */
187: private long _lastStart;
188:
189: /**
190: * The duration of the most recent call. Initially <code>0L</code>.
191: */
192: private long _lastDuration;
193:
194: /**
195: * The total duration of all calls up until now. Initially
196: * <code>0L</code>.
197: */
198: private long _duration;
199:
200: /**
201: * The minimum time a call took. Initially set to
202: * {@link Long#MAX_VALUE}.
203: */
204: private long _min = Long.MAX_VALUE;
205:
206: /**
207: * The start time of the call that took the shortest. Initially
208: * <code>0L</code>.
209: */
210: private long _minStart;
211:
212: /**
213: * The duration of the call that took the longest. Initially
214: * <code>0L</code>.
215: */
216: private long _max;
217:
218: /**
219: * The start time of the call that took the longest. Initially
220: * <code>0L</code>.
221: */
222: private long _maxStart;
223:
224: /**
225: * Constructs a new <code>Statistic</code> object.
226: */
227: private Statistic() {
228: _min = Long.MAX_VALUE;
229: }
230:
231: /**
232: * Records a call.
233: *
234: * @param start
235: * the start time, in milliseconds since the UNIX Epoch, not
236: * <code>null</code>.
237: *
238: * @param duration
239: * duration of the call, in milliseconds since the
240: * <a href="http://en.wikipedia.org/wiki/Unix_Epoch">UNIX Epoch</a>.
241: */
242: public synchronized void recordCall(long start, long duration) {
243: _lastStart = start;
244: _lastDuration = duration;
245: _calls++;
246: _duration += duration;
247: _min = _min > duration ? duration : _min;
248: _max = _max < duration ? duration : _max;
249: _minStart = (_min == duration) ? start : _minStart;
250: _maxStart = (_max == duration) ? start : _maxStart;
251: }
252:
253: /**
254: * Get this statistic as an {@link Element}.
255: *
256: * @param successful
257: * true if the result is successful, false otherwise.
258: * @param errorCode
259: * the errorCode of the unsuccessful result, if you want it also
260: * specified in the returned element.
261: *
262: * @return
263: * the statistic, cannot be <code>null</code>
264: */
265: public synchronized Element getElement(boolean successful,
266: String errorCode) {
267:
268: String average;
269: String min;
270: String minStart;
271: String max;
272: String maxStart;
273: String lastStart;
274: String lastDuration;
275: if (_calls == 0) {
276: average = NOT_AVAILABLE;
277: min = NOT_AVAILABLE;
278: minStart = NOT_AVAILABLE;
279: max = NOT_AVAILABLE;
280: maxStart = NOT_AVAILABLE;
281: lastStart = NOT_AVAILABLE;
282: lastDuration = NOT_AVAILABLE;
283: } else if (_duration == 0) {
284: average = "0";
285: min = String.valueOf(_min);
286: minStart = DateConverter.toDateString(TIME_ZONE,
287: _minStart);
288: max = String.valueOf(_max);
289: maxStart = DateConverter.toDateString(TIME_ZONE,
290: _maxStart);
291: lastStart = DateConverter.toDateString(TIME_ZONE,
292: _lastStart);
293: lastDuration = String.valueOf(_lastDuration);
294: } else {
295: average = String.valueOf(_duration / _calls);
296: min = String.valueOf(_min);
297: minStart = DateConverter.toDateString(TIME_ZONE,
298: _minStart);
299: max = String.valueOf(_max);
300: maxStart = DateConverter.toDateString(TIME_ZONE,
301: _maxStart);
302: lastStart = DateConverter.toDateString(TIME_ZONE,
303: _lastStart);
304: lastDuration = String.valueOf(_lastDuration);
305: }
306: ElementBuilder element = new ElementBuilder(
307: successful ? "successful" : "unsuccessful");
308: element.setAttribute("count", String.valueOf(_calls));
309: element.setAttribute("average", average);
310: if (errorCode != null) {
311: element.setAttribute("errorcode", errorCode);
312: }
313: ElementBuilder minElem = new ElementBuilder("min");
314: minElem.setAttribute("start", minStart);
315: minElem.setAttribute("duration", min);
316: element.addChild(minElem.createElement());
317: ElementBuilder maxElem = new ElementBuilder("max");
318: maxElem.setAttribute("start", maxStart);
319: maxElem.setAttribute("duration", max);
320: element.addChild(maxElem.createElement());
321: ElementBuilder lastElem = new ElementBuilder("last");
322: lastElem.setAttribute("start", lastStart);
323: lastElem.setAttribute("duration", lastDuration);
324: element.addChild(lastElem.createElement());
325: return element.createElement();
326: }
327:
328: /**
329: * Resets this statistic.
330: */
331: public synchronized void reset() {
332: _calls = 0;
333: _lastStart = 0L;
334: _lastDuration = 0L;
335: _duration = 0L;
336: _min = Long.MAX_VALUE;
337: _minStart = 0L;
338: _max = 0L;
339: _maxStart = 0L;
340: }
341: }
342: }
|