0

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.

Cristian G
  • 460
  • 1
  • 4
  • 22

3 Answers3

2

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
Godin
  • 9,801
  • 2
  • 39
  • 76
  • Thank you so much for the effort you put into this message. I was able to do it 5 minutes ago - I've done it via `simplejmx` library which did not work in the past due to my jdk version being 8 instead of 10. I will accept this as the answer as it uses mostly the parts I've used, I will also add an answer with my version. – Cristian G May 28 '19 at 11:35
1

Please check the link - How to get a thread and heap dump of a Java process on Windows that's not running in a console , where i have posted an answer in which programmatically heap is dumped by using JMX & Reflection. You can refer the program as a sample. This code works from JDK 6 onwards.

Ramesh Subramanian
  • 944
  • 1
  • 12
  • 28
0

I did not try @Godin's version but I am sure it is working since it is so detailed.

I have done it a different way: I have used the simplejmx library available at https://jar-download.com/?search_box=simplejmx.

In order to attach the JmxClient to the process you need to know the port it is running on. It can be set via the JVM arguments as such:

-Dcom.sun.management.jmxremote.port=1240
-Dcom.sun.management.jmxremote.local.only=false
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

Once the app is running on the specified port the following script will access it and invoke a target operation:

public static void main(String[] args) throws Exception {
    JmxClient client = new JmxClient("localhost", 1240);
    client.invokeOperation(new ObjectName("org.jacoco:type=Runtime"), "dump", new Object[] {true});
    client.close();
}

Also check: How to programmatically check JMX MBean operations and attributes?

Note: It must be on jdk version 10 or higher. Does not work on 8.

Cristian G
  • 460
  • 1
  • 4
  • 22