package com.maxifier.guice.mbean;
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.PreDestroy;
import javax.management.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
@Singleton
public class MBeanManagerImpl implements MBeanManager {
private static Logger logger = LoggerFactory.getLogger(MBeanManagerImpl.class);
private final Collection<ObjectName> mbeans = new HashSet<ObjectName>();
private final String domain;
private final MBeanGenerator mbeanGenerator;
private final MBeanServer mbeanServer;
public MBeanManagerImpl(String domain, MBeanServer mbeanServer, MBeanGenerator mBeanGenerator) {
this.mbeanServer = mbeanServer;
//add current time
this.domain = domain;
this.mbeanGenerator = mBeanGenerator;
addShutdownHook();
}
@Override
public synchronized void register(Object... mbean) {
if (mbean.length > 0) {
register(Arrays.asList(mbean));
}
}
@Override
public void register(Iterable<Object> mbeans) {
for (Object mbean : mbeans) {
String name = resolveName(mbean);
if (!checkCompliantion(mbean.getClass())) {
try {
mbean = mbeanGenerator.makeMBean(mbean);
} catch (MBeanGenerationException e) {
logger.warn(String.format("Unable to register mbean %s," +
" instance is not compliant with JMX spec and mbean generation has been failed", mbean), e);
}
}
register(name, mbean);
}
}
@Override
public synchronized void register(String name, Object mbean) {
try {
ObjectName objectName = prepareObjectName(name);
try {
mbeanServer.registerMBean(mbean, objectName);
} catch (InstanceAlreadyExistsException e) {
logger.warn("Instance with name {} is already exists, second instance will be registered instead of first", name);
try {
mbeanServer.unregisterMBean(objectName);
} catch (InstanceNotFoundException e1) {
//NOP
}
mbeanServer.registerMBean(mbean, objectName);
}
mbeans.add(objectName);
} catch (MalformedObjectNameException e) {
logger.warn(String.format("Unable to register %s mbean, wrong name", mbean), e);
} catch (MBeanRegistrationException e) {
logger.warn(String.format("Unable to register mbean %s", mbean), e);
} catch (NotCompliantMBeanException e) {
logger.warn(String.format("Unable to register mbean %s, wrong mbean class", mbean), e);
} catch (InstanceAlreadyExistsException e) {
logger.warn(String.format("Unable to register mbean %s, instance already exists", mbean), e);
}
}
@Override
public synchronized void unregister(String... name) {
if (name.length > 0) {
unregister(Arrays.asList(name));
}
}
@Override
public void unregister(Iterable<String> names) {
for (String name : names) {
try {
unregisterMBean(prepareObjectName(name));
} catch (MalformedObjectNameException e) {
logger.error("MBean unregistration error", e);
}
}
}
@PreDestroy
//it's here for supply lifecycle
public synchronized void unregisterAll() {
//new set to prevent CME
for (ObjectName objectName : new HashSet<ObjectName>(mbeans)) {
unregisterMBean(objectName);
}
}
String resolveName(Object mbean) {
String name = null;
Class<?> mbeanClass = mbean.getClass();
MBean mbeanAnnotation = mbeanClass.getAnnotation(MBean.class);
if (mbeanAnnotation != null) {
name = mbeanAnnotation.name();
}
if (isBlank(name)) {
name = makeDefaultName(mbeanClass);
}
return checkAlreadyDomained(name);
}
String checkAlreadyDomained(String mbeanName) {
final int index = mbeanName.indexOf(':');
if (index > -1) {
logger.warn("Managed bean name already include domain." +
" MBeanManager domain '{}' will be used instead.", domain);
return mbeanName.substring(index + 1);
}
return mbeanName;
}
String makeDefaultName(Class<?> mbeanClass) {
return String.format("class=%s", mbeanClass.getName());
}
ObjectName prepareObjectName(String name) throws MalformedObjectNameException {
return new ObjectName(String.format("%s:%s", domain, name));
}
private boolean isBlank(String name) {
return name == null || name.trim().isEmpty();
}
private void unregisterMBean(ObjectName objectName) {
try {
mbeanServer.unregisterMBean(objectName);
} catch (Exception e) {
logger.warn("MBean unregistration error", e);
}
mbeans.remove(objectName);
}
private void addShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
unregister();
}
}));
}
static boolean checkCompliantion(Class baseClass) {
Class current = baseClass;
Class mbeanInterface = null;
while (current != null) {
mbeanInterface =
findMBeanInterface(current, current.getName());
if (mbeanInterface != null) break;
current = current.getSuperclass();
}
return mbeanInterface != null;
}
private static Class findMBeanInterface(Class aClass, String aName) {
Class current = aClass;
while (current != null) {
final Class[] interfaces = current.getInterfaces();
final int len = interfaces.length;
for (int i = 0; i < len; i++) {
final Class inter =
implementsMBean(interfaces[i], aName);
if (inter != null) return inter;
}
current = current.getSuperclass();
}
return null;
}
private static Class implementsMBean(Class c, String clName) {
String clMBeanName = clName + "MBean";
if (c.getName().equals(clMBeanName)) {
return c;
}
Class[] interfaces = c.getInterfaces();
for (Class anInterface : interfaces) {
if (anInterface.getName().equals(clMBeanName))
return anInterface;
}
return null;
}
}