/*
* Copyright (c) 2012-2014, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.common.monitoring;
import java.lang.management.ManagementFactory;
import java.lang.ref.WeakReference;
import java.util.Date;
import javax.management.AttributeChangeNotification;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.StandardEmitterMBean;
import javax.management.timer.Timer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
/**
*
* @author pron
*/
public abstract class PeriodicMonitor extends StandardEmitterMBean implements NotificationListener, NotificationEmitter, InitializingBean, DisposableBean {
private static final Logger LOG = LoggerFactory.getLogger(PeriodicMonitor.class);
private final String name;
private boolean registered;
private long lastCollectTime;
private int notificationSequenceNumber;
private WeakReference<Object> monitored;
private boolean timerStarted;
private int timerPeriod = 5000;
private final Timer timer = new Timer();
public PeriodicMonitor(Class mbeanInterface, String name) {
super(mbeanInterface, true, new NotificationBroadcasterSupport());
this.name = name;
this.lastCollectTime = System.currentTimeMillis();
this.monitored = null;
}
@Override
public void afterPropertiesSet() throws Exception {
collectAndResetCounters1();
registerMBean();
}
@Override
public void destroy() throws Exception {
unregisterMBean();
}
public void setMonitoredObject(Object obj) {
this.monitored = new WeakReference<Object>(obj);
}
private boolean isMonitoredObjectAlive() {
return monitored == null || monitored.get() != null;
}
private void registerMBean() {
try {
LOG.info("Registering MBean {}", name);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName mxbeanName = new ObjectName(name);
mbs.registerMBean(this, mxbeanName);
timer.addNotificationListener(this, new NotificationFilter() {
@Override
public boolean isNotificationEnabled(Notification notification) {
return "tickTimer".equals(notification.getType());
}
}, null);
this.registered = true;
} catch (InstanceAlreadyExistsException ex) {
throw new RuntimeException(ex);
} catch (MBeanRegistrationException ex) {
throw new RuntimeException(ex);
} catch (NotCompliantMBeanException ex) {
throw new AssertionError(ex);
} catch (MalformedObjectNameException ex) {
throw new AssertionError(ex);
}
}
public void unregisterMBean() {
try {
if (registered) {
LOG.info("Unregistering MBean {}", name);
ManagementFactory.getPlatformMBeanServer().unregisterMBean(new ObjectName(name));
timer.stop();
}
this.registered = false;
} catch (Exception e) {
LOG.warn("Exception:", e);
}
}
@Override
public MBeanNotificationInfo[] getNotificationInfo() {
String[] types = new String[]{
AttributeChangeNotification.ATTRIBUTE_CHANGE
};
String _name = AttributeChangeNotification.class.getName();
String description = "An attribute of this MBean has changed";
MBeanNotificationInfo info = new MBeanNotificationInfo(types, _name, description);
return new MBeanNotificationInfo[]{info};
}
@Override
public void handleNotification(Notification notification, Object handback) {
if ("tickTimer".equals(notification.getType())) {
//assert Objects.equal(handback, name);
refresh();
}
}
public void refresh() {
collectAndResetCounters1();
//Notification n = new AttributeChangeNotification(this, notificationSequenceNumber++, System.currentTimeMillis(), "CacheInfo changed", "PerfInfo", newValue.getClass().getName(), null, newValue);
Notification n = new AttributeChangeNotification(this, notificationSequenceNumber++, System.currentTimeMillis(), "Info changed", "", null, null, null);
sendNotification(n);
}
protected long getMillisSinceLastCollect() {
return System.currentTimeMillis() - lastCollectTime;
}
public synchronized int getTimerPeriod() {
return timerPeriod;
}
public synchronized void setTimerPeriod(int timerPeriod) {
if (timerPeriod != this.timerPeriod) {
this.timerPeriod = timerPeriod;
if (timerStarted) {
stopUpdates();
startUpdates();
}
}
}
public synchronized boolean isUpdates() {
return timerStarted;
}
public synchronized void setUpdates(boolean value) {
if (value == timerStarted)
return;
if (!timerStarted)
startUpdates();
else
stopUpdates();
}
public synchronized void startUpdates() {
if (!timerStarted) {
timer.addNotification("tickTimer", null, null, new Date(System.currentTimeMillis()), timerPeriod);
this.timerStarted = true;
timer.start();
}
}
public synchronized void stopUpdates() {
if (timerStarted) {
try {
timer.removeNotifications("tickTimer");
this.timerStarted = false;
} catch (InstanceNotFoundException ex) {
}
timer.stop();
}
}
private void collectAndResetCounters1() {
if (registered) {
if (!isMonitoredObjectAlive())
unregisterMBean();
else {
collectAndResetCounters();
lastCollectTime = System.currentTimeMillis();
return;
}
}
resetCounters();
lastCollectTime = System.currentTimeMillis();
}
protected void initCounters() {
}
protected abstract void collectAndResetCounters();
protected abstract void resetCounters();
}