2c48bab91a
JmxMBeanServer is a concrete implementation of a MBeanServer. We want to use it directly because we need to bypass calls to JmxMBeanServer.registerMBean and JmxMBeanServer.unregisterMBean. They take ObjectName as parameter, copy it using ObjectName.getInstance(ObjectName) and pass it to registerMBean and unregisterMBean of JmxMBeanServer.getMBeanServerInterceptor(). We want to avoid this copy and pass the ObjectName argument directly to JmxMBeanServer.getMBeanServerInterceptor(). To do that this patch: 1. changes all MBeanServer variables to JmxMBeanServer 2. creates JmxMBeanServer in APIBuilder making sure accessing interceptors is allowed 3. makes sure that JmxMBeanServer.getMBeanServerInterceptor().registerMBean is called wherever JmxMBeanServer.registerMBean was called 4. makes sure that JmxMBeanServer.getMBeanServerInterceptor().unregisterMBean is called whenever JmxMBeanServer.unregisterMBean was called Next patch will use different ObjectName implementation that will use less memory and this patch is crucial because without it every ObjectName is transformed with ObjectName.getInstance which turns the object into a regular ObjectName. Signed-off-by: Piotr Jastrzebski <piotr@scylladb.com>
191 lines
6.2 KiB
Java
191 lines
6.2 KiB
Java
package com.scylladb.jmx.metrics;
|
|
|
|
import java.lang.reflect.Field;
|
|
import java.util.Set;
|
|
import java.util.function.Function;
|
|
import java.util.function.Predicate;
|
|
|
|
import javax.management.BadAttributeValueExpException;
|
|
import javax.management.BadBinaryOpValueExpException;
|
|
import javax.management.BadStringOperationException;
|
|
import javax.management.InstanceAlreadyExistsException;
|
|
import javax.management.InstanceNotFoundException;
|
|
import javax.management.InvalidApplicationException;
|
|
import javax.management.MBeanRegistration;
|
|
import javax.management.MBeanRegistrationException;
|
|
import javax.management.MBeanServer;
|
|
import javax.management.MalformedObjectNameException;
|
|
import javax.management.NotCompliantMBeanException;
|
|
import javax.management.ObjectName;
|
|
import javax.management.QueryExp;
|
|
|
|
import com.scylladb.jmx.api.APIClient;
|
|
import com.sun.jmx.mbeanserver.JmxMBeanServer;
|
|
|
|
/**
|
|
* Base type for MBeans in scylla-jmx. Wraps auto naming and {@link APIClient}
|
|
* holding.
|
|
*
|
|
* @author calle
|
|
*
|
|
*/
|
|
public class APIMBean implements MBeanRegistration {
|
|
protected final APIClient client;
|
|
protected final String mbeanName;
|
|
|
|
public APIMBean(APIClient client) {
|
|
this(null, client);
|
|
}
|
|
|
|
public APIMBean(String mbeanName, APIClient client) {
|
|
this.mbeanName = mbeanName;
|
|
this.client = client;
|
|
}
|
|
|
|
/**
|
|
* Helper method to add/remove dynamically created MBeans from a server
|
|
* instance.
|
|
*
|
|
* @param server
|
|
* The {@link MBeanServer} to check
|
|
* @param all
|
|
* All {@link ObjectName}s that should be bound
|
|
* @param predicate
|
|
* {@link QueryExp} predicate to filter relevant object names.
|
|
* @param generator
|
|
* {@link Function} to create a new MBean instance for a given
|
|
* {@link ObjectName}
|
|
*
|
|
* @return
|
|
* @throws MalformedObjectNameException
|
|
*/
|
|
public static boolean checkRegistration(JmxMBeanServer server, Set<ObjectName> all,
|
|
final Predicate<ObjectName> predicate, Function<ObjectName, Object> generator)
|
|
throws MalformedObjectNameException {
|
|
Set<ObjectName> registered = queryNames(server, predicate);
|
|
for (ObjectName name : registered) {
|
|
if (!all.contains(name)) {
|
|
try {
|
|
server.getMBeanServerInterceptor().unregisterMBean(name);
|
|
} catch (MBeanRegistrationException | InstanceNotFoundException e) {
|
|
}
|
|
}
|
|
}
|
|
|
|
int added = 0;
|
|
for (ObjectName name : all) {
|
|
if (!registered.contains(name)) {
|
|
try {
|
|
server.getMBeanServerInterceptor().registerMBean(generator.apply(name), name);
|
|
added++;
|
|
} catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException e) {
|
|
}
|
|
}
|
|
}
|
|
return added > 0;
|
|
}
|
|
|
|
/**
|
|
* Helper method to query {@link ObjectName}s from an {@link MBeanServer}
|
|
* based on {@link Predicate}
|
|
*
|
|
* @param server
|
|
* @param predicate
|
|
* @return
|
|
*/
|
|
public static Set<ObjectName> queryNames(JmxMBeanServer server, final Predicate<ObjectName> predicate) {
|
|
@SuppressWarnings("serial")
|
|
Set<ObjectName> registered = server.queryNames(null, new QueryExp() {
|
|
@Override
|
|
public void setMBeanServer(MBeanServer s) {
|
|
}
|
|
|
|
@Override
|
|
public boolean apply(ObjectName name) throws BadStringOperationException, BadBinaryOpValueExpException,
|
|
BadAttributeValueExpException, InvalidApplicationException {
|
|
return predicate.test(name);
|
|
}
|
|
});
|
|
return registered;
|
|
}
|
|
|
|
JmxMBeanServer server;
|
|
ObjectName name;
|
|
|
|
protected final ObjectName getBoundName() {
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* Figure out an {@link ObjectName} for this object based on either
|
|
* contructor parameter, static field, or just package/class name.
|
|
*
|
|
* @return
|
|
* @throws MalformedObjectNameException
|
|
*/
|
|
protected ObjectName generateName() throws MalformedObjectNameException {
|
|
String mbeanName = this.mbeanName;
|
|
if (mbeanName == null) {
|
|
Field f;
|
|
try {
|
|
f = getClass().getDeclaredField("MBEAN_NAME");
|
|
f.setAccessible(true);
|
|
mbeanName = (String) f.get(null);
|
|
} catch (Throwable t) {
|
|
}
|
|
}
|
|
if (mbeanName == null) {
|
|
for (Class<?> c : getClass().getInterfaces()) {
|
|
Field f;
|
|
try {
|
|
f = c.getDeclaredField("OBJECT_NAME");
|
|
f.setAccessible(true);
|
|
mbeanName = (String) f.get(null);
|
|
break;
|
|
} catch (Throwable t) {
|
|
}
|
|
}
|
|
}
|
|
if (mbeanName == null) {
|
|
String name = getClass().getName();
|
|
int i = name.lastIndexOf('.');
|
|
mbeanName = name.substring(0, i) + ":type=" + name.substring(i + 1);
|
|
}
|
|
return new ObjectName(mbeanName);
|
|
}
|
|
|
|
/**
|
|
* Keeps track of bound server and optionally generates an
|
|
* {@link ObjectName} for this instance.
|
|
*/
|
|
@Override
|
|
public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
|
|
if (this.server != null) {
|
|
throw new IllegalStateException("Can only exist in a single MBeanServer");
|
|
}
|
|
this.server = (JmxMBeanServer) server;
|
|
if (name == null) {
|
|
name = generateName();
|
|
}
|
|
this.name = name;
|
|
|
|
return name;
|
|
}
|
|
|
|
@Override
|
|
public void postRegister(Boolean registrationDone) {
|
|
}
|
|
|
|
@Override
|
|
public void preDeregister() throws Exception {
|
|
}
|
|
|
|
@Override
|
|
public void postDeregister() {
|
|
assert server != null;
|
|
assert name != null;
|
|
this.server = null;
|
|
this.name = null;
|
|
}
|
|
}
|