package me.prettyprint.cassandra.service;
import java.lang.management.ManagementFactory;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.management.*;
import me.prettyprint.cassandra.connection.HConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A JMX monitor singlton.
*
* @author Ran Tavory (ran@outbain.com)
*
*/
public class JmxMonitor {
private final Logger log = LoggerFactory.getLogger(getClass());
private MBeanServer mbs;
private static JmxMonitor monitorInstance;
private Map<String,CassandraClientMonitor> monitors;
private JmxMonitor() {
mbs = ManagementFactory.getPlatformMBeanServer();
monitors = new HashMap<String, CassandraClientMonitor>();
}
public static JmxMonitor getInstance() {
if ( monitorInstance == null ) {
monitorInstance = new JmxMonitor();
}
return monitorInstance;
}
public void registerMonitor(String name, String monitorType, Object monitoringInterface)
throws MalformedObjectNameException, InstanceAlreadyExistsException,
MBeanRegistrationException, NotCompliantMBeanException {
String monitorName = generateMonitorName(name, monitorType);
log.info("Registering JMX {}", monitorName);
ObjectName oName = new ObjectName(monitorName);
// Check if the monitor is already registered
if (mbs.isRegistered(oName)) {
log.info("Monitor already registered: {}", oName);
return;
}
mbs.registerMBean(monitoringInterface, oName);
}
public void unregisterMonitor(String name, String monitorType)
throws MalformedObjectNameException, InstanceAlreadyExistsException,
MBeanRegistrationException, NotCompliantMBeanException {
String monitorName = generateMonitorName(name, monitorType);
log.info("Unregistering JMX {}", monitorName);
ObjectName oName = new ObjectName(monitorName);
// Check if the monitor is already registered
if (!mbs.isRegistered(oName)) {
log.info("Monitor is not registered: {}", oName);
return;
}
try
{
mbs.unregisterMBean(oName);
}
catch(InstanceNotFoundException e)
{
log.warn("Failed to unregister monitor: {}" + oName.toString(), e);
}
}
private String generateMonitorName(String className, String monitorType) {
StringBuilder sb = new StringBuilder();
sb.append(className);
sb.append(":ServiceType=");
// append the classloader name so we have unique names in web apps.
sb.append(getUniqueClassloaderIdentifier());
if (null != monitorType && monitorType.length() > 0) {
sb.append(",MonitorType=" + monitorType);
}
return sb.toString();
}
/**
* Generates a unique, but still nice and predictable name representing this classloader so that
* even apps operating under a web server such as tomcat with multiple classloaders would bee able
* to register each with its own unique mbean.
*/
private String getUniqueClassloaderIdentifier() {
String contextPath = getContextPath();
if (contextPath != null) {
return contextPath;
}
return "hector";
}
/**
* Tries to guess a context path for the running application.
* If this is a web application running under a tomcat server this will work.
* If unsuccessful, returns null.
* @return A string representing the current context path or null if it cannot be determined.
*/
private String getContextPath() {
ClassLoader loader = getClass().getClassLoader();
if(loader == null)
return null;
URL url = loader.getResource("/");
if (url != null) {
String[] elements = url.toString().split("/");
for (int i = elements.length - 1; i > 0; --i) {
// URLs look like this: file:/.../ImageServer/WEB-INF/classes/
// And we want that part that's just before WEB-INF
if ("WEB-INF".equals(elements[i])) {
return elements[i - 1];
}
}
}
return null;
}
public CassandraClientMonitor getCassandraMonitor(HConnectionManager connectionManager) {
CassandraClientMonitor cassandraClientMonitor = monitors.get(connectionManager.getClusterName());
if ( cassandraClientMonitor == null ) {
try {
cassandraClientMonitor = new CassandraClientMonitor(connectionManager);
registerMonitor("me.prettyprint.cassandra.service_"+connectionManager.getClusterName(), "hector",
cassandraClientMonitor);
monitors.put(connectionManager.getClusterName(), cassandraClientMonitor);
} catch (MalformedObjectNameException e) {
log.error("Unable to register JMX monitor", e);
} catch (InstanceAlreadyExistsException e) {
log.error("Unable to register JMX monitor", e);
} catch (MBeanRegistrationException e) {
log.error("Unable to register JMX monitor", e);
} catch (NotCompliantMBeanException e) {
log.error("Unable to register JMX monitor", e);
}
}
return cassandraClientMonitor;
}
public void removeCassandraMonitor(HConnectionManager connectionManager) {
CassandraClientMonitor cassandraClientMonitor = monitors.remove(connectionManager.getClusterName());
if(cassandraClientMonitor != null) {
try
{
unregisterMonitor("me.prettyprint.cassandra.service_"+connectionManager.getClusterName(), "hector");
}
catch(MalformedObjectNameException e)
{
log.error("Unable to unregister JMX monitor", e);
}
catch(InstanceAlreadyExistsException e)
{
log.error("Unable to unregister JMX monitor", e);
}
catch(MBeanRegistrationException e)
{
log.error("Unable to unregister JMX monitor", e);
}
catch(NotCompliantMBeanException e)
{
log.error("Unable to unregister JMX monitor", e);
}
}
}
}