如何创建一个简单的 JMX 客户端?

How to create a simple JMX client?

假设我们在服务器端有预定义 API 的 MBean。我想直接使用 API 从客户机调用该 MBean。如何创建简单的 JMX 客户端以通过 RMI 获取远程 java 对象?

用法

private CacheMBean<String, Message> messageStorage = newJmxClient(  
    CacheMBean.class,   
    "com.my.company:type=cache,name=MessageCacheImpl",   
    "service:jmx:rmi:///jndi/rmi://<host>:<port>/karaf-root",   
    "smx",   
    "smx");
...
Message message = messageStorage.get(messageId);

实施

import com.google.common.collect.ImmutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.Map;

import javax.management.InstanceAlreadyExistsException;
import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.StandardMBean;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public final class JmxUtils {

    private static final Logger LOG = LoggerFactory.getLogger(JmxUtils.class);

    private JmxUtils() {
    }

    public static <T> T newJmxClient(Class<T> clazz, String objectName, String serviceUrl) {
        return newJmxClient(clazz, objectName, serviceUrl, null, null);
    }

    public static <T> T newJmxClient(Class<T> clazz, String objectName, String serviceUrl, final String user, final String pass) {
        try {
            JMXServiceURL jmxServiceUrl = new JMXServiceURL(serviceUrl);
            Map<String, ?> env = user == null ? null : ImmutableMap.of(JMXConnector.CREDENTIALS, new String[] {user, pass});
            JMXConnector jmxc = JMXConnectorFactory.connect(jmxServiceUrl, env);
            MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
            ObjectName mbeanName = new ObjectName(objectName);
            return JMX.newMBeanProxy(mbsc, mbeanName, clazz, true);
        } catch (IOException | MalformedObjectNameException e) {
            throw new RuntimeException("Can not create client for remote JMX " + serviceUrl, e);
        }
    }

    /**
     * @param objName
     *            domain:type=value[,name=value]
     * @param implementation
     * @param mbeanInterface
     * @see ObjectName
     * @see StandardMBean
     */
    public static <I> ObjectInstance registerMBean(String objName, I implementation, Class<I> mbeanInterface) {
        int counter = 0;
        String uniqueSuffix = "";
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        while (true) {
            try {
                ObjectName name = new ObjectName(objName + uniqueSuffix);
                StandardMBean mbean = new StandardMBean(implementation, mbeanInterface);
                return mbs.registerMBean(mbean, name);
            } catch (InstanceAlreadyExistsException e) {
                LOG.trace("JMX instance exists, trying next available", e);
                uniqueSuffix = "" + ++counter;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * @param objName
     * @see ObjectName
     */
    public static void unregisterMBean(String objName) {
        try {
            final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
            final ObjectName name = new ObjectName(objName);
            mbs.unregisterMBean(name);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}