001: /*
002: * Copyright (C) The MX4J Contributors.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the MX4J License version 1.0.
006: * See the terms of the MX4J License in the documentation provided with this software.
007: */
008:
009: package mx4j.tools.stats;
010:
011: import java.util.Date;
012: import java.util.SortedMap;
013: import java.util.TreeMap;
014: import javax.management.MBeanRegistration;
015: import javax.management.MBeanServer;
016: import javax.management.ObjectName;
017:
018: import mx4j.log.Log;
019: import mx4j.log.Logger;
020:
021: /**
022: * Class AbstractStatisticsRecorder. Abstract Parent of the Stats collector
023: * classes. It implements some basic services
024: *
025: * @version $Revision: 1.6 $
026: * @see StatisticsRecorderMBean
027: */
028: public abstract class AbstractStatisticsRecorder implements
029: StatisticsRecorderMBean, MBeanRegistration {
030: /* Indicates whether the Monitor is active */
031: protected boolean isActive = false;
032:
033: /* MBeanServer reference */
034: protected MBeanServer server;
035:
036: /* Maximum amount of entries */
037: protected int maxEntries = 256;
038:
039: /* Holds the entries */
040: protected SortedMap entries = new TreeMap();
041:
042: /* Initial recording date */
043: protected Date recordingStart;
044:
045: /* Indicates if the type of the recorded value is double */
046: protected boolean isDouble = false;
047:
048: /* Statistical values */
049: protected double minimumValue, maximumValue, averageValue;
050:
051: /* Count of recorded values */
052: protected long count = 0;
053:
054: protected Logger getLogger() {
055: return Log.getLogger(getClass().getName());
056: }
057:
058: public void start() {
059: Logger logger = getLogger();
060: if (!isActive) {
061: if (logger.isEnabledFor(Logger.DEBUG))
062: logger.debug("Starting statistics recorder " + this );
063: this .isActive = true;
064: recordingStart = new Date();
065: entries.clear();
066: minimumValue = maximumValue = averageValue = 0;
067: count = 0;
068: isDouble = false;
069: try {
070: doStart();
071: } catch (Exception e) {
072: logger.error("Exception while starting recorder "
073: + this , e);
074: }
075: }
076: }
077:
078: public void stop() {
079: Logger logger = getLogger();
080: if (isActive) {
081: if (logger.isEnabledFor(Logger.DEBUG))
082: logger.debug("Starting statistics recorder " + this );
083: this .isActive = false;
084: try {
085: doStop();
086: } catch (Exception e) {
087: logger.error("Exception starting recorder " + this , e);
088: }
089: }
090: }
091:
092: public Number getAverage() {
093: return createValue(averageValue);
094: }
095:
096: public Number getMin() {
097: return createValue(minimumValue);
098: }
099:
100: public Number getMax() {
101: return createValue(maximumValue);
102: }
103:
104: public synchronized boolean isActive() {
105: return isActive;
106: }
107:
108: public int getMaxEntries() {
109: return maxEntries;
110: }
111:
112: public void setMaxEntries(int maxEntries) {
113: if (maxEntries <= 0) {
114: throw new IllegalArgumentException(
115: "Max entries has to be bigger than 0");
116: }
117: this .maxEntries = maxEntries;
118: }
119:
120: public SortedMap getEntries() {
121: return (SortedMap) ((TreeMap) entries).clone();
122: }
123:
124: public Date getRecordingStart() {
125: return recordingStart;
126: }
127:
128: public ObjectName preRegister(MBeanServer server, ObjectName name)
129: throws Exception {
130: this .server = server;
131: return name;
132: }
133:
134: public void postRegister(Boolean registrationDone) {
135: }
136:
137: public void preDeregister() throws Exception {
138: this .stop();
139: }
140:
141: public void postDeregister() {
142: }
143:
144: /**
145: * Subclasses may override this to offer a custom startup procedure
146: */
147: protected void doStart() throws Exception {
148: }
149:
150: /**
151: * Subclasses may override this to offer a custom stop procedure
152: */
153: protected void doStop() throws Exception {
154: }
155:
156: /**
157: * Adds an entry to the collection. It also reduces the size if too big
158: * and updates the statics
159: */
160: protected synchronized void addEntry(Date key, Number value) {
161: if (isActive) {
162: entries.put(new PointTime(key, count++), value);
163: if (entries.size() > maxEntries) {
164: while (entries.size() > maxEntries) {
165: entries.remove(entries.firstKey());
166: }
167: }
168: calculateStats(value);
169: }
170: }
171:
172: /**
173: * Updates the statistics
174: */
175: private void calculateStats(Number value) {
176: if (!isDouble
177: && (value instanceof Double || value instanceof Float)) {
178: isDouble = true;
179: }
180: double newValue = value.doubleValue();
181:
182: if (count == 1) {
183: maximumValue = minimumValue = averageValue = newValue;
184: return;
185: }
186: if (newValue > maximumValue) {
187: maximumValue = newValue;
188: }
189: if (newValue < minimumValue) {
190: minimumValue = newValue;
191: }
192: averageValue = (1 - 1 / (double) count) * averageValue + 1
193: / (double) count * newValue;
194: }
195:
196: private Number createValue(double targetValue) {
197: if (isDouble) {
198: return new Double(targetValue);
199: } else {
200: return new Long((long) targetValue);
201: }
202: }
203:
204: }
|