package org.jboss.monitor.services;
import org.jboss.system.ServiceMBeanSupport;
import javax.management.ObjectName;
import javax.management.Notification;
import org.jboss.monitor.alarm.Alarm;
import org.jboss.monitor.alarm.AlarmManager;
import org.jboss.monitor.alarm.MBeanImplAccess;
public class MemoryMonitor
extends ServiceMBeanSupport
implements MemoryMonitorMBean
{
public static final String MEMORY_LOW = "jboss.alarm.memory.low";
public static final String FREE_MEMORY_KEY = "freeMemory";
public static final String DEFAULT_WARNING_THRESHOLD = "5m";
public static final int DEFAULT_WARNING_MEASUREMENTS = 3;
public static final String DEFAULT_CRITICAL_THRESHOLD = "2m";
public static final String DEFAULT_SAMPLING_PERIOD = "5sec";
public static final long KILO = 1024;
public static final long MEGA = 1024 * 1024;
public static final long GIGA = 1024 * 1024 * 1024;
public static final long SECS = 1000; public static final long MINS = 60 * 1000;
public static final long HOUR = 60 * 60 * 1000;
private long wThreshold;
private String wThresholdString;
private int wMeasurements;
private long cThreshold;
private String cThresholdString;
private long samplingPeriod;
private String samplingPeriodString;
private boolean isStopRequested;
private long freeMemory;
private int warningSamples;
AlarmManager alm =
new AlarmManager(
new MBeanImplAccess() {
public ObjectName getMBeanName() { return getServiceName(); }
public long getSequenceNumber() { return getNextNotificationSequenceNumber(); }
public void emitNotification(Notification n) { sendNotification(n); }
});
public MemoryMonitor()
{
setFreeMemoryWarningThreshold(DEFAULT_WARNING_THRESHOLD);
setFreeMemoryCriticalThreshold(DEFAULT_CRITICAL_THRESHOLD);
setSamplingPeriod(DEFAULT_SAMPLING_PERIOD);
this.wMeasurements = DEFAULT_WARNING_MEASUREMENTS;
}
public void setTriggeringWarningMeasurements(int measurements)
{
if (measurements > 0)
this.wMeasurements = measurements;
}
public int getTriggeringWarningMeasurements()
{
return this.wMeasurements;
}
public void setFreeMemoryWarningThreshold(String s)
{
synchronized (this) {
this.wThreshold = parseMemorySpec(s);
this.wThresholdString = s;
}
}
public String getFreeMemoryWarningThreshold()
{
return this.wThresholdString;
}
public void setFreeMemoryCriticalThreshold(String s)
{
synchronized (this) {
this.cThreshold = parseMemorySpec(s);
this.cThresholdString = s;
}
}
public String getFreeMemoryCriticalThreshold()
{
return this.cThresholdString;
}
public void setSamplingPeriod(String s)
{
synchronized (this) {
this.samplingPeriod = parseTimePeriod(s);
this.samplingPeriodString = s;
}
}
public String getSamplingPeriod()
{
return this.samplingPeriodString;
}
public long getFreeMemorySample()
{
synchronized (this) {
return this.freeMemory;
}
}
public String getSeverity()
{
return alm.getSeverityAsString(MEMORY_LOW);
}
public void startService()
throws Exception
{
Runnable r = new Runnable() {
public void run()
{
log.info("Starting memory monitor thread" +
", samplingPeriod=" + MemoryMonitor.this.samplingPeriodString +
", warningThreshold=" + MemoryMonitor.this.wThresholdString +
", criticalThreshold=" + MemoryMonitor.this.cThresholdString);
long wThreshold;
long cThreshold;
long samplingPeriod;
synchronized(MemoryMonitor.this) {
wThreshold = MemoryMonitor.this.wThreshold;
cThreshold = MemoryMonitor.this.cThreshold;
samplingPeriod = MemoryMonitor.this.samplingPeriod;
}
warningSamples = wMeasurements;
while (!isStopRequested)
{
sampleMemory(wThreshold, cThreshold);
if (!isStopRequested) {
try {
Thread.sleep(samplingPeriod);
}
catch (InterruptedException e) {
}
}
}
log.info("Stopping memory monitor thread");
}
};
if (this.cThreshold > this.wThreshold)
throw new Exception(
"FreeMemoryWarningThreshold (" + this.wThreshold +
") set lower than FreeMemoryCriticalThreshold (" + this.cThreshold + ")");
isStopRequested = false;
Thread t = new Thread(r, "Memory monitor thread of \"" + getServiceName() + "\"");
t.start();
}
public void stopService()
{
this.isStopRequested = true;
}
private void sampleMemory(long wThreshold, long cThreshold)
{
long freeMemory = Runtime.getRuntime().freeMemory();
synchronized (this) {
this.freeMemory = freeMemory;
};
if (freeMemory <= cThreshold) { alm.setAlarm(
MEMORY_LOW,
Alarm.SEVERITY_CRITICAL,
"Free memory in critical state!",
FREE_MEMORY_KEY,
new Long(freeMemory)
);
warningSamples = wMeasurements;
}
else if (freeMemory <= wThreshold) {
if (warningSamples > 0)
--warningSamples;
if (warningSamples == 0
|| alm.getSeverity(MEMORY_LOW) == Alarm.SEVERITY_CRITICAL) {
alm.setAlarm(
MEMORY_LOW,
Alarm.SEVERITY_WARNING,
"Free memory getting low!",
FREE_MEMORY_KEY,
new Long(freeMemory)
);
}
}
else {
alm.setAlarm(
MEMORY_LOW,
Alarm.SEVERITY_NORMAL,
"Free memory at normal levels!",
FREE_MEMORY_KEY,
new Long(freeMemory)
);
warningSamples = wMeasurements;
}
}
private static long parseMemorySpec(String s)
{
try {
int len = s.length();
long factor = 0;
switch (s.charAt(len - 1)) {
case 'k':
case 'K':
factor = KILO;
s = s.substring(0, len - 1);
break;
case 'm':
case 'M':
factor = MEGA;
s = s.substring(0, len - 1);
break;
case 'g':
case 'G':
factor = GIGA;
s = s.substring(0, len - 1);
break;
default:
factor = 1;
break;
}
long retval = Long.parseLong(s) * factor;
if (retval < 0)
throw new NumberFormatException();
return retval;
}
catch (RuntimeException e) {
throw new NumberFormatException("Not a valid memory specification: " + s);
}
}
private static long parseTimePeriod(String s)
{
try {
s = s.toLowerCase();
long factor = 0;
if (s.endsWith("sec")) {
s = s.substring(0, s.lastIndexOf("sec"));
factor = SECS;
}
else if (s.endsWith("min")) {
s = s.substring(0, s.lastIndexOf("min"));
factor = MINS;
}
else if (s.endsWith("h")) {
s = s.substring(0, s.lastIndexOf("h"));
factor = HOUR;
}
else
factor = 1;
long retval = Long.parseLong(s) * factor;
if (retval < 0)
throw new NumberFormatException();
return retval;
}
catch (RuntimeException e) {
throw new NumberFormatException("Not a valid time period specification: " + s);
}
}
}