How to set JMX remote port system environment parameters through java code for remote monitoring?
Asked Answered
C

6

9

I have a program which requires dynamically (i.e. at run time) opening an available socket and start a JMX agent on it. This JMX parameters are being set inside the Java code and not through command line. This works fine. Thereafter it is needed to monitor( i.e issue JMX commands etc) through Java Visual VM remotely

The RMI server agent in the program is on the lines of out of box management described at: http://download.oracle.com/javase/6/docs/technotes/guides/management/agent.html

The question I have can be summarized as: How can such command line properties be set to the system level through the Java code, so that remote profiling can be used??

-Dcom.sun.management.jmxremote.port=1234

If the "jmxremote.port" and other parameters are set through command line, remote monitoring works fine. I am trying to find a way to do this through Java and not through the command line.

The program can not specify the port through the command line as the new available port has to be figured out at run time.

The process needs remote monitoring and it works fine locally. If the following parameters are not specified at command line, Java Visual VM does not connect to the process.

-Dcom.sun.management.jmxremote.port=1234
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Djava.rmi.server.hostname=10.0.0.128

I have tried.

System.setProperty("com.sun.management.jmxremote.port",Integer.toString(port));

This is one of the first things done in the program before starting the JMXConnectorServer. Unfortunately it is not recognized. Only the run time properties (i.e. specified through command line are recognized for JMX connection by Java Visual VM).

Also came across the way properties can be extracted from java collection classes but could not reach how to trace the property "com.sun.management.jmxremote.port="

public static void setEnv(Map<String, String> newenv) throws Exception {
  Class[] classes = Collections.class.getDeclaredClasses();
  Map<String, String> env = System.getenv();

  for(Class cl : classes) {

    if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {

      Field field = cl.getDeclaredField("m");
      field.setAccessible(true);

      Object obj = field.get(env);
      Map<String, String> map = (Map<String, String>) obj;

      //map.clear();
      map.putAll(newenv);
    }
  }
}

Any help would be appreciated!

Croak answered 1/9, 2011 at 21:38 Comment(1)
You should edit your original question and include the code samples that you have been posting in comments.Extranuclear
B
11

kbec's answer showed the way but did not work for me - however by looking at this post I was able to modify it and get a working solution.

public static String loadJMXAgent(int port) throws IOException,
        AttachNotSupportedException, AgentLoadException,
        AgentInitializationException {
    String name = ManagementFactory.getRuntimeMXBean().getName();
    VirtualMachine vm = VirtualMachine.attach(name.substring(0,
            name.indexOf('@')));

    String lca = vm.getAgentProperties().getProperty(
            "com.sun.management.jmxremote.localConnectorAddress");
    if (lca == null) {
        Path p = Paths.get(System.getProperty("java.home")).normalize();
        if (!"jre".equals(p.getName(p.getNameCount() - 1).toString()
                .toLowerCase())) {
            p = p.resolve("jre");
        }
        File f = p.resolve("lib").resolve("management-agent.jar").toFile();
        if (!f.exists()) {
            throw new IOException("Management agent not found");
        }
        String options = String.format("com.sun.management.jmxremote.port=%d, " +
                "com.sun.management.jmxremote.authenticate=false, " +
                "com.sun.management.jmxremote.ssl=false", port);
        vm.loadAgent(f.getCanonicalPath(), options);
        lca = vm.getAgentProperties().getProperty(
                "com.sun.management.jmxremote.localConnectorAddress");
    }
    vm.detach();
    return lca;
}

This works in Eclipse however getting it to work at the command line is a different matter - there is some discussion about this here Why does using the Java Attach API fail on linux? (even though maven build completes) but I found adding $JAVA_HOME/lib/tools.jar to my classpath solved the problem.

Baisden answered 23/7, 2013 at 1:26 Comment(1)
"com.sun.management.jmxremote.localConnectorAddress" property does not created. but this code worked for meBarrel
A
4

You're going about this slightly the wrong way. By the time your code is called you've missed the chance for these properties to have any effect.

You need to create an RmiRegistry and then create a JMXConnectorServer linked to the platform MBeanServer like this:

private void createJmxConnectorServer() throws IOException {
    LocateRegistry.createRegistry(1234);
    MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
    JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://localhost/jndi/rmi://localhost:1234/jmxrmi");
    JMXConnectorServer svr = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
    svr.start();
}
Adieu answered 18/3, 2016 at 13:0 Comment(0)
G
2

If you don't specify any jmxremote env as run param then JMX management agent was not loaded. You can try this for dynamic loading :

public static String loadJMXAgent(int port) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
    System.setProperty("com.sun.management.jmxremote.port", Integer.toString(port));
    String name = ManagementFactory.getRuntimeMXBean().getName();
    VirtualMachine vm = VirtualMachine.attach(name.substring(0, name.indexOf('@')));

    String lca = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
    if (lca == null) {
        Path p = Paths.get(System.getProperty("java.home")).normalize();
        if (!"jre".equals(p.getName(p.getNameCount()-1).toString().toLowerCase())) p = p.resolve("jre");
        File f = p.resolve("lib").resolve("management-agent.jar").toFile();
        if (!f.exists()) throw new IOException("Management agent not found");

        vm.loadAgent(f.getCanonicalPath(), "com.sun.management.jmxremote");
        lca = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
    }
    vm.detach();
    return lca;
}

You must include jdk/lib/tools.jar

Gert answered 20/6, 2012 at 18:28 Comment(0)
R
1

I tried some ways to specify the jmxremote port from java code to have a connection on a specific port and have figured out the following:

In case jmxremote arg is specified: The platform mbean server is started by the JVM, before my code modifies the necessary jmxremote System.properties. Every mbean server has an own registry of beans. The plaform and JVM mbeans could not register their own beans to it on an other way.

You can create an alternate mbean server after you set up the jmx port properties. That will listen on the correct jmx port, you specified.

This way you choose the platform server :

MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

This way your own :

System.setProperty("com.sun.management.jmxremote.port","9991");
//...
MBeanServer mbsCustom=MBeanServerFactory.createMBeanServer();

also consider that linux has it's loopback interface so you should specify the correct hostname explicitly to listen on.

This is not recommended to use an other MBeanServer than the platform's, according to manuals, but I can imagine some situations where the command line options are not the way you can launch a server.

Rigadoon answered 6/2, 2014 at 12:58 Comment(0)
T
0

System.setProperty() is identical to the -D command line option. However obviously you must call it early enough that you are setting the property before it is being read.

Tanishatanitansy answered 2/9, 2011 at 0:35 Comment(2)
The property which are being read by the profiler for remote profiling are the default properties set at the time JVM starts. In this case when the java code starts. I am starting the java visual vm profiler much later and setting the property before JMXConnectorServer is started. Trying to figure out how to over ride the property so that it is reflected as a jmx system level and can be detected by java visual vm ?? Would appreciate any suggestions on how to call it "early enough".Croak
Running app without any -Dcom.sun.management.jmxremote and setting this env in runtime later is bad idea because JMX agent was not loaded, so it cannot be used.Gert
C
0

This is what works for me. Reference Oracle JMX Tutorial. I am assuming you already know how to right SimpleMXBean used in below example.

package sample;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.util.HashMap;
import java.util.Map;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;

public class MBServerTest {
    public static void loadJMXAgent(int port, MBeanServer mbs) throws IOException  {
        LocateRegistry.createRegistry(port);
        System.out.println("Initialize the environment map");
        Map<String,Object> env = new HashMap<String,Object>();
        env.put("com.sun.management.jmxremote.authenticate", "false");
        env.put("com.sun.management.jmxremote.ssl", "false");
        System.out.println("Create an RMI connector server");
        JMXServiceURL url =
            new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:"+port+"/jmxrmi");
        JMXConnectorServer cs =
            JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);

        // Start the RMI connector server.
        //
        System.out.println("Start the RMI connector server");
        cs.start();

    }

    public static void main(String[] args) throws Exception {
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        loadJMXAgent(1199,mbs);

        SimpleStandard cache = new SimpleStandard();

        ObjectName name = new ObjectName(
                "org.javalobby.tnt.jmx:type=ApplicationCacheMBean");
        mbs.registerMBean(cache, name);
        imitateActivity(cache);
    }

    private static void imitateActivity(SimpleStandard cache) {
        while (true) {
            try {
                cache.cacheObject("hello");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
        }
    }
}
Cytolysin answered 12/4, 2014 at 18:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.