How to connect to a java program on localhost jvm using JMX?
Asked Answered
E

5

45

I should connect to a java program on localhost jvm using JMX. In other words I want to develop a JMX client to config a java program on localhost.

  • Don't recommend using JConsole! JConsole is not suitable because it is general JMX client and have negative effect on main program performance.

  • Samples on oracle site use RMIConnector and host:port params but I don't know: where should set jmx port?

  • JConsole have an option to connect to java processes by PID. But I don't find any method in JMX api that have PID as input param.

Est answered 5/4, 2011 at 13:52 Comment(2)
Both main program and jmx client are stand-alone programs (Java SE).Est
Also see pongasoft.com/blog/yan/entry/connecting_to_a_local_vmSejant
B
64

We use something like the following to programatically connect to our JMX servers. You should run your server with something like the following arguments:

-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.port=1234
-Dcom.sun.management.jmxremote.ssl=false

To bind to a particular address you'll need to add the following VM arguments:

-Djava.rmi.server.hostname=A.B.C.D

Then you can connect to your server using JMX client code like the following:

String host = "localhost";  // or some A.B.C.D
int port = 1234;
String url = "service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/jmxrmi";
JMXServiceURL serviceUrl = new JMXServiceURL(url);
JMXConnector jmxConnector = JMXConnectorFactory.connect(serviceUrl, null);
try {
   MBeanServerConnection mbeanConn = jmxConnector.getMBeanServerConnection();
   // now query to get the beans or whatever
   Set<ObjectName> beanSet = mbeanConn.queryNames(null, null);
   ...
} finally {
   jmxConnector.close();
}

We also have code that can programatically publish itself to a particular port outside of the VM arguments but that's more fu than you need I think.


In terms of connecting "by pid", you need to be using Java6 to do it from Java land as far as I know. I've not used the following code but it seems to work.

List<VirtualMachineDescriptor> vms = VirtualMachine.list();
for (VirtualMachineDescriptor desc : vms) {
    VirtualMachine vm;
    try {
        vm = VirtualMachine.attach(desc);
    } catch (AttachNotSupportedException e) {
        continue;
    }
    Properties props = vm.getAgentProperties();
    String connectorAddress =
        props.getProperty("com.sun.management.jmxremote.localConnectorAddress");
    if (connectorAddress == null) {
        continue;
    }
    JMXServiceURL url = new JMXServiceURL(connectorAddress);
    JMXConnector connector = JMXConnectorFactory.connect(url);
    try {
        MBeanServerConnection mbeanConn = connector.getMBeanServerConnection();
        Set<ObjectName> beanSet = mbeanConn.queryNames(null, null);
        ...
    } finally {
        jmxConnector.close();
    }
}

I've also the author of SimpleJMX package which makes it easy to start a JMX server and publish beans to remote clients.

// create a new server listening on port 8000
JmxServer jmxServer = new JmxServer(8000);
// start our server
jmxServer.start();
// register our lookupCache object defined below
jmxServer.register(lookupCache);
jmxServer.register(someOtherObject);
// stop our server
jmxServer.stop();

It does have a client interface as well but right now it doesn't have any mechanisms to find processes by PID -- only host/port combinations are supported (in 6/2012).

Bigname answered 5/4, 2011 at 16:16 Comment(3)
Thanks Gray! Can you (or other peoples) answer my second question(local jmx connecting using PID)?Est
If for some reason the ConnectorAddress is null, you can try to load the agent dinamically. See pongasoft.com/blog/yan/entry/connecting_to_a_local_vmTerris
you have a compilation issue in the line: jmxConnector.close();Maleeny
D
7

To clarify, if you are only interested in getting local JMX stats, you don't need to use the remote api. Just use java.lang.management.ManagementFactory:

MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
memoryMXBean.getHeapMemoryUsage().getMax();
...

List<MemoryPoolMXBean> beans = ManagementFactory.getMemoryPoolMXBeans();
...
Ditch answered 29/1, 2013 at 16:57 Comment(0)
B
3
List<VirtualMachineDescriptor> vm = new ArrayList<VirtualMachineDescriptor>();
        jvmList = new JVMListManager();

        vm = jvmList.listActiveVM();

        for (VirtualMachineDescriptor vmD : vm) 
        {
            try
            {

            //importFrom is taking a process ID and returning a service url in a String Format
            String ServiceUrl = ConnectorAddressLink.importFrom(Integer.parseInt(vmD.id().trim()));
            JMXServiceURL jmxServiceUrl = new JMXServiceURL(ServiceUrl);

            jmxConnector = JMXConnectorFactory.connect(jmxServiceUrl, null);
            con = jmxConnector.getMBeanServerConnection();
            CompilationMXBean compMXBean = ManagementFactory.newPlatformMXBeanProxy(con
                   , ManagementFactory.COMPILATION_MXBEAN_NAME
                   , CompilationMXBean.class);
            }catch(Exception e)
            {
            //Do Something  
            }
        }


protected List listActiveVM() {
    List<VirtualMachineDescriptor> vm = VirtualMachine.list();

    return vm;
}

This requires you to use the jmxremote argument at JVM startup for the process you are trying to read. TO be able to do it without having to pass a jmxremote argument at startup. You will have to use the attach api(only applicable for Programs using Java 6 and higher.

Bothy answered 28/12, 2012 at 9:51 Comment(0)
E
2

Simplest means:

import javax.management.Attribute;
import javax.management.AttributeList;
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;

// set a self JMX connection
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
// set the object name(s) you are willing to query, here a CAMEL JMX object
ObjectName objn = new ObjectName("org.apache.camel:context=*,type=routes,name=\"route*\"");
Set<ObjectName> objectInstanceNames = mBeanServer.queryNames(objn, null);
for (ObjectName on : objectInstanceNames) {
    // query a number of attributes at once
    AttributeList attrs = mBeanServer.getAttributes(on, new String[] {"ExchangesCompleted","ExchangesFailed"});
    // process attribute values (beware of nulls...)
    // ... attrs.get(0) ... attrs.get(1) ...
}
Ezar answered 7/11, 2016 at 16:4 Comment(0)
B
1

This is how you can get a JMX connection to a Java Program with it's PID (for version <= Java 8 only) :

import sun.management.ConnectorAddressLink;
import javax.management.*;

public static MBeanServerConnection getLocalJavaProcessMBeanServer(int javaProcessPID) throws IOException {
    String address = ConnectorAddressLink.importFrom(javaProcessPID);
    JMXServiceURL jmxUrl = new JMXServiceURL(address);
    return JMXConnectorFactory.connect(jmxUrl).getMBeanServerConnection();
}
Benevento answered 14/6, 2020 at 9:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.