001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.monitor.services;
023:
024: import org.jboss.system.ServiceMBeanSupport;
025: import org.jboss.util.Strings;
026:
027: import javax.management.ObjectName;
028: import javax.management.Notification;
029:
030: import org.jboss.monitor.alarm.Alarm;
031: import org.jboss.monitor.alarm.AlarmManager;
032: import org.jboss.monitor.alarm.MBeanImplAccess;
033:
034: /**
035: * MemoryMonitor class.
036: *
037: * @jmx:mbean
038: * extends="org.jboss.system.ServiceMBean"
039: *
040: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
041: * @version $Revision: 57210 $
042: */
043: public class MemoryMonitor extends ServiceMBeanSupport implements
044: MemoryMonitorMBean {
045: // Constants -----------------------------------------------------
046:
047: /** Notification type which indicates a memory low alarm */
048: public static final String MEMORY_LOW = "jboss.alarm.memory.low";
049:
050: /** Free memory key to use in AlarmNotification/userData map */
051: public static final String FREE_MEMORY_KEY = "freeMemory";
052:
053: /** default warning threshold */
054: public static final String DEFAULT_WARNING_THRESHOLD = "5m";
055:
056: /** default number of measurements to trigger warning */
057: public static final int DEFAULT_WARNING_MEASUREMENTS = 3;
058:
059: /** default critical threshold */
060: public static final String DEFAULT_CRITICAL_THRESHOLD = "2m";
061:
062: /** default sampling period */
063: public static final String DEFAULT_SAMPLING_PERIOD = "5sec";
064:
065: /** conversion constants */
066: public static final long KILO = 1024;
067: public static final long MEGA = 1024 * 1024;
068: public static final long GIGA = 1024 * 1024 * 1024;
069:
070: // Private -------------------------------------------------------
071:
072: /** warning threshold */
073: private long wThreshold;
074:
075: /** warning threshold stringfied */
076: private String wThresholdString;
077:
078: /** number of measurements in the warning area before warning is sent */
079: private int wMeasurements;
080:
081: /** critical threshold */
082: private long cThreshold;
083:
084: /** critical threshold stringfied */
085: private String cThresholdString;
086:
087: /** memory sampling period */
088: private long samplingPeriod;
089:
090: /** sampling period stringfied */
091: private String samplingPeriodString;
092:
093: /** control sampling thread */
094: private boolean isStopRequested;
095:
096: /** last measurement of free memory */
097: private long freeMemory;
098:
099: /** number of samples in the warning area */
100: private int warningSamples;
101:
102: /** alarm manager */
103: AlarmManager alm = new AlarmManager(new MBeanImplAccess() {
104: public ObjectName getMBeanName() {
105: return getServiceName();
106: }
107:
108: public long getSequenceNumber() {
109: return getNextNotificationSequenceNumber();
110: }
111:
112: public void emitNotification(Notification n) {
113: sendNotification(n);
114: }
115: });
116:
117: // Constructors --------------------------------------------------
118:
119: /**
120: * CTOR
121: */
122: public MemoryMonitor() {
123: // setup default values
124: setFreeMemoryWarningThreshold(DEFAULT_WARNING_THRESHOLD);
125: setFreeMemoryCriticalThreshold(DEFAULT_CRITICAL_THRESHOLD);
126: setSamplingPeriod(DEFAULT_SAMPLING_PERIOD);
127: this .wMeasurements = DEFAULT_WARNING_MEASUREMENTS;
128: }
129:
130: // Attributes --------------------------------------------------
131:
132: /**
133: * @jmx:managed-attribute
134: */
135: public void setTriggeringWarningMeasurements(int measurements) {
136: if (measurements > 0) {
137: this .wMeasurements = measurements;
138: }
139: }
140:
141: /**
142: * @jmx:managed-attribute
143: */
144: public int getTriggeringWarningMeasurements() {
145: return this .wMeasurements;
146: }
147:
148: /**
149: * @jmx:managed-attribute
150: */
151: public void setFreeMemoryWarningThreshold(String s) {
152: synchronized (this ) {
153: this .wThreshold = parseMemorySpec(s);
154: this .wThresholdString = s;
155: }
156: }
157:
158: /**
159: * @jmx:managed-attribute
160: */
161: public String getFreeMemoryWarningThreshold() {
162: return this .wThresholdString;
163: }
164:
165: /**
166: * @jmx:managed-attribute
167: */
168: public void setFreeMemoryCriticalThreshold(String s) {
169: synchronized (this ) {
170: this .cThreshold = parseMemorySpec(s);
171: this .cThresholdString = s;
172: }
173: }
174:
175: /**
176: * @jmx:managed-attribute
177: */
178: public String getFreeMemoryCriticalThreshold() {
179: return this .cThresholdString;
180: }
181:
182: /**
183: * @jmx:managed-attribute
184: */
185: public void setSamplingPeriod(String s) {
186: synchronized (this ) {
187: this .samplingPeriod = Strings.parsePositiveTimePeriod(s);
188: this .samplingPeriodString = s;
189: }
190: }
191:
192: /**
193: * @jmx:managed-attribute
194: */
195: public String getSamplingPeriod() {
196: return this .samplingPeriodString;
197: }
198:
199: /**
200: * @jmx:managed-attribute
201: */
202: public long getFreeMemorySample() {
203: synchronized (this ) {
204: return this .freeMemory;
205: }
206: }
207:
208: /**
209: * @jmx:managed-attribute
210: */
211: public String getSeverity() {
212: return alm.getSeverityAsString(MEMORY_LOW);
213: }
214:
215: // Service Lifecycle ---------------------------------------------
216:
217: public void startService() throws Exception {
218: // Annonymous class
219: Runnable r = new Runnable() {
220: public void run() {
221: log.debug("Started memory monitor thread"
222: + ", samplingPeriod="
223: + MemoryMonitor.this .samplingPeriodString
224: + ", warningThreshold="
225: + MemoryMonitor.this .wThresholdString
226: + ", criticalThreshold="
227: + MemoryMonitor.this .cThresholdString);
228:
229: // make copies of config params
230: long wThreshold;
231: long cThreshold;
232: long samplingPeriod;
233:
234: synchronized (MemoryMonitor.this ) {
235: wThreshold = MemoryMonitor.this .wThreshold;
236: cThreshold = MemoryMonitor.this .cThreshold;
237: samplingPeriod = MemoryMonitor.this .samplingPeriod;
238: }
239:
240: // initialise warningSamples countdown
241: warningSamples = wMeasurements;
242:
243: while (!isStopRequested) {
244: sampleMemory(wThreshold, cThreshold);
245:
246: if (!isStopRequested) {
247: try {
248: Thread.sleep(samplingPeriod);
249: } catch (InterruptedException e) {
250: // ignored
251: }
252: }
253: }
254: log.debug("Stopped memory monitor thread");
255: }
256: };
257:
258: // check for validity
259: if (this .cThreshold > this .wThreshold) {
260: throw new Exception("FreeMemoryWarningThreshold ("
261: + this .wThreshold
262: + ") set lower than FreeMemoryCriticalThreshold ("
263: + this .cThreshold + ")");
264: }
265: isStopRequested = false;
266: Thread t = new Thread(r, "Memory monitor thread of \""
267: + getServiceName() + "\"");
268: t.start();
269: }
270:
271: public void stopService() {
272: // signal thread to stop
273: this .isStopRequested = true;
274: }
275:
276: // Private Methods -----------------------------------------------
277:
278: /**
279: * The real stuff
280: */
281: private void sampleMemory(long wThreshold, long cThreshold) {
282: long freeMemory = Runtime.getRuntime().freeMemory();
283:
284: synchronized (this ) {
285: this .freeMemory = freeMemory;
286: }
287: ;
288:
289: if (freeMemory <= cThreshold) { // critical
290: alm.setAlarm(MEMORY_LOW, Alarm.SEVERITY_CRITICAL,
291: "Free memory in critical state", FREE_MEMORY_KEY,
292: new Long(freeMemory));
293: // reset warning countdown
294: warningSamples = wMeasurements;
295: } else if (freeMemory <= wThreshold) {
296: if (warningSamples > 0) {
297: --warningSamples;
298: }
299: if (warningSamples == 0
300: || alm.getSeverity(MEMORY_LOW) == Alarm.SEVERITY_CRITICAL) {
301: alm.setAlarm(MEMORY_LOW, Alarm.SEVERITY_WARNING,
302: "Free memory getting low", FREE_MEMORY_KEY,
303: new Long(freeMemory));
304: }
305: } else {
306: alm.setAlarm(MEMORY_LOW, Alarm.SEVERITY_NORMAL,
307: "Free memory at normal levels", FREE_MEMORY_KEY,
308: new Long(freeMemory));
309: // reset warning countdown
310: warningSamples = wMeasurements;
311: }
312: }
313:
314: /**
315: * Parses a memory specification into a long.
316: *
317: * Translates the [kK|mM|gG] suffixes
318: *
319: * For example:
320: * "10" -> 10 (bytes)
321: * "10k" -> 10240 (bytes)
322: * "10m" -> 10485760 (bytes)
323: * "10g" -> 10737418240 (bytes)
324: */
325: private static long parseMemorySpec(String s) {
326: try {
327: int len = s.length();
328: long factor = 0;
329:
330: switch (s.charAt(len - 1)) {
331: case 'k':
332: case 'K':
333: factor = KILO;
334: s = s.substring(0, len - 1);
335: break;
336:
337: case 'm':
338: case 'M':
339: factor = MEGA;
340: s = s.substring(0, len - 1);
341: break;
342:
343: case 'g':
344: case 'G':
345: factor = GIGA;
346: s = s.substring(0, len - 1);
347: break;
348:
349: default:
350: factor = 1;
351: break;
352: }
353: long retval = Long.parseLong(s) * factor;
354:
355: if (retval < 0) {
356: throw new NumberFormatException();
357: }
358: return retval;
359: } catch (RuntimeException e) {
360: throw new NumberFormatException(
361: "Not a valid memory specification: " + s);
362: }
363: }
364:
365: }
|