Search code examples
javajmxjacocojconsolembeans

How to programmatically invoke MBeans operation of target process


I want to simulate what jconsole does by:

  1. Identifying a running process that exposes jmx functionality (e.g: pid: 14796 FreshProject.jar)

  2. Listing the available MBeans libraries (e.g: org.jacoco)

  3. Invoking an operation (e.g: clicking dump)

MBeans Operation example

I've tried using the simplejmx library as mentioned here but I am confused as to what hostName and port I should use. I tried passing localhost and 1099 as I've read these are the default values but it errors java.net.ConnectException: Connection refused: connect.

Please don't point me to a different sof post and close this as I most probably already read and tried it several times.


Solution

  • I want to simulate what jconsole does

    Have a look at the implementation of jconsole - see http://openjdk.java.net/tools/svc/jconsole/ and https://github.com/openjdk/jdk/tree/master/src/jdk.jconsole/share/classes

    I am confused as to what hostName and port I should use. I tried passing localhost and 1099 as I've read these are the default values but it errors java.net.ConnectException: Connection refused: connect.

    As per https://docs.oracle.com/en/java/javase/11/management/monitoring-and-management-using-jmx-technology.html by default there is no port. And JConsole uses Attach API - see https://github.com/openjdk/jdk/blob/master/src/jdk.jconsole/share/classes/sun/tools/jconsole/LocalVirtualMachine.java In this code you'll also find answer on

    Identifying a running process that exposes jmx functionality

    To connect via port you need to specify appropriate parameters. For example following Example.java

    class Example {
        public static void main(String[] args) {
            while (true) {
            }
        }
    }
    

    can be started as

    java \
        -Dcom.sun.management.jmxremote.port=1099 \
        -Dcom.sun.management.jmxremote.authenticate=false \
        -Dcom.sun.management.jmxremote.ssl=false \
        Example
    

    Then

    Listing the available MBeans

    can be done as

    import javax.management.MBeanServer;
    import javax.management.MBeanServerConnection;
    import javax.management.MBeanServerInvocationHandler;
    import javax.management.ObjectInstance;
    import javax.management.ObjectName;
    import javax.management.remote.JMXConnector;
    import javax.management.remote.JMXConnectorFactory;
    import javax.management.remote.JMXServiceURL;
    
    import java.lang.management.ManagementFactory;
    import java.util.Iterator;
    import java.util.Set;
    
    class GetMBeans {
        public static void main(final String[] args) throws Exception {
           final JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
           final JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
           final MBeanServerConnection connection = jmxc.getMBeanServerConnection();
    
           Set<ObjectInstance> instances = connection.queryMBeans(null, null);
           Iterator<ObjectInstance> iterator = instances.iterator();
           while (iterator.hasNext()) {
               ObjectInstance instance = iterator.next();
               System.out.println(instance.getClassName() + " " + instance.getObjectName());
           }
        }
    }
    

    After start of the above Example also with JaCoCo

    java \
        -Dcom.sun.management.jmxremote.port=1099 \
        -Dcom.sun.management.jmxremote.authenticate=false \
        -Dcom.sun.management.jmxremote.ssl=false \
        -javaagent:jacoco-0.8.4/lib/jacocoagent.jar=jmx=true \
        Example
    

    execution of javac GetMBeans.java && java GetMBeans | grep jacoco produces

    org.jacoco.agent.rt.internal_035b120.Agent org.jacoco:type=Runtime
    

    Invoking an operation

    is shown in JaCoCo documentation - see MBeanClient.java at https://www.jacoco.org/jacoco/trunk/doc/api.html

    import javax.management.MBeanServer;
    import javax.management.MBeanServerConnection;
    import javax.management.MBeanServerInvocationHandler;
    import javax.management.ObjectInstance;
    import javax.management.ObjectName;
    import javax.management.remote.JMXConnector;
    import javax.management.remote.JMXConnectorFactory;
    import javax.management.remote.JMXServiceURL;
    
    import java.lang.management.ManagementFactory;
    import java.util.Iterator;
    import java.util.Set;
    
    class MBeanClient {
        public static void main(final String[] args) throws Exception {
           final JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
           final JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
           final MBeanServerConnection connection = jmxc.getMBeanServerConnection();
    
           final IProxy proxy = (IProxy) MBeanServerInvocationHandler.newProxyInstance(connection, new ObjectName("org.jacoco:type=Runtime"), IProxy.class, false);
    
           final byte[] data = proxy.getExecutionData(false);
           System.out.println("Got " + data.length + " bytes");
        }
    
        public interface IProxy {
            String getVersion();
            String getSessionId();
            void setSessionId(String id);
            byte[] getExecutionData(boolean reset);
            void dump(boolean reset);
            void reset();
        }
    }
    

    execution of javac MBeanClient.java && java MBeanClient produces

    Got 84 bytes