Mimicking the out-of-the-box JMX management agent in J2SE 5.0

Starting with J2SE 5.0, the JVM is instrumented for monitoring and management providing built-in ("out-of-the-box") management capabilities for both remote and local access. The JVM includes a platform MBean server and platform MBeans that JMX management applications can use. You can also instrument and monitor your own applications by adding your application specific MBeans to the platform MBean server.

By default, remote access to the out-of-the-box management agent is protected by authentication (password files), authorization (access files) and SSL/TLS encryption. All the out-of-the-box management agent configuration is performed through system properties and/or through a management.properties file (located under JRE_HOME/lib/management/management.properties). For more detailed info about the Monitoring and Management APIs in the Java platform have a look at the Monitoring and Management Guide.

In most of the cases using the out-of-the-box management agent and configuring it through the management.properties file is enough but there are some cases such as when exporting the RMI server remote objects in a given port to allow firewall traversing, or exporting the RMI server remote objects using a specific network interface in multi-homed systems, etc, where it may not be enough. For such cases we can mimic the behavior of the out-of-the-box management agent by directly using the JMX Remote API to programmatically create, configure and deploy the management agent.


Let's see how to implement a JMX agent that identically mimics the out-of-the-box management agent which runs on port 3000 with a password file named password.properties, an access file named access.properties and the default configuration for the SSL/TLS-based RMI Socket Factories which require server authentication only. This example assumes a keystore has been already created. More detailed information about how to set up all the SSL configuration can be found in the JSSE Reference Guide.

To start your application and enable the out-of-the-box management agent using system properties you would run:

java -Dcom.sun.management.jmxremote.port=3000 \\
     -Dcom.sun.management.jmxremote.password.file=password.properties \\
     -Dcom.sun.management.jmxremote.access.file=access.properties \\
     -Djavax.net.ssl.keyStore=keystore \\
     -Djavax.net.ssl.keyStorePassword=password \\
     MyApp

NOTE: The com.sun.management.jmxremote.\* properties could have been specified in a management.properties file instead of passing them in the command line. The system property -Dcom.sun.management.config.file=management.properties would then be required to specify the management.properties file location.


Let's simulate now the same behavior by programmatically starting a management agent in your application. This is the code you would write to achieve that:

import java.lang.management.\*;
import java.rmi.registry.\*;
import java.util.\*;
import javax.management.\*;
import javax.management.remote.\*;
import javax.management.remote.rmi.\*;
import javax.rmi.ssl.\*;

public class MyApp {

    public static void main(String[] args) throws Exception {

        // Ensure cryptographically strong random number generator used
        // to choose the object number - see java.rmi.server.ObjID
        //
        System.setProperty("java.rmi.server.randomIDs", "true");

        // Start an RMI registry on port 3000.
        //
        System.out.println("Create RMI registry on port 3000");
        LocateRegistry.createRegistry(3000);

        // Retrieve the PlatformMBeanServer.
        //
        System.out.println("Get the platform's MBean server");
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

        // Environment map.
        //
        System.out.println("Initialize the environment map");
        HashMap<String,Object> env = new HashMap<String,Object>();

        // Provide SSL-based RMI socket factories.
        //
        // The protocol and cipher suites to be enabled will be the ones
        // defined by the default JSSE implementation and only server
        // authentication will be required.
        //
        SslRMIClientSocketFactory csf = new SslRMIClientSocketFactory();
        SslRMIServerSocketFactory ssf = new SslRMIServerSocketFactory();
        env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf);
        env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, ssf);

        // Provide the password file used by the connector server to
        // perform user authentication. The password file is a properties
        // based text file specifying username/password pairs.
        //
        env.put("jmx.remote.x.password.file", "password.properties");

        // Provide the access level file used by the connector server to
        // perform user authorization. The access level file is a properties
        // based text file specifying username/access level pairs where
        // access level is either "readonly" or "readwrite" access to the
        // MBeanServer operations.
        //
        env.put("jmx.remote.x.access.file", "access.properties");

        // Create an RMI connector server.
        //
        // As specified in the JMXServiceURL the RMIServer stub will be
        // registered in the RMI registry running in the local host on
        // port 3000 with the name "jmxrmi". This is the same name the
        // out-of-the-box management agent uses to register the RMIServer
        // stub too.
        //
        System.out.println("Create an RMI connector server");
        JMXServiceURL url =
            new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:3000/jmxrmi");
        JMXConnectorServer cs =
            JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);

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

To start your application you would run:

java -Djavax.net.ssl.keyStore=keystore \\
     -Djavax.net.ssl.keyStorePassword=password \\
     MyApp

NOTE 1: There's a slight but important difference between the RMI registry used by the out-of-the-box management agent and the one used by the management agent mimicking it. The difference is that the RMI registry used by the out-of-the-box management agent is read-only, i.e. a single entry can be bound to it and once bound it cannot be unbound.

NOTE 2: Both RMI registries are insecure as they don't use SSL/TLS. They should be created using SSL/TLS-based RMI Socket Factories which require client authentication. This prevents a client from sending its credentials to a rogue RMIServer and it prevents the RMI registry to give access to the RMIServer stub to an untrusted client.

This can be achieved programmatically both in J2SE 5.0 and Java SE 6 by creating the RMI registry as follows:

LocateRegistry.createRegistry(3000,
                              new SslRMIClientSocketFactory(),
                              new SslRMIServerSocketFactory(null, null, true));

This is a known limitation of the out-of-the-box management agent in J2SE 5.0. In Java SE 6 you would need to add the following two properties to your management.properties file to achieve that behavior:

com.sun.management.jmxremote.registry.ssl=true
com.sun.management.jmxremote.ssl.need.client.auth=true

I hope by now it is a bit clearer how the out-of-the-box management agent works and the code necessary to mimic its main behavior. Of course, this example does not cover all the existing properties in the management.properties file and the MyApp java class would have to be modified accordingly if other properties were specified. If you encounter a limitation in the out-of-the-box management agent for which there's no available workaround let us know but also consider this java class as a starting point for implementing your management solution in a programmatic way.


Comments:

[Trackback] ... I've seen a few posts in the Java and JMX forums from developers who were wondering how to find out why JConsole wouldn't connect to their application. So I have decided to write this short blog entry in order to outline a few diagnosing ...

Posted by JMX, SNMP, Java, etc... on June 02, 2006 at 11:19 AM CEST #

how can i set the username/password (as required by the jconsole gui) programmatically?

thanks for you help
phileas

Posted by Phileas Fogg on August 14, 2008 at 10:36 AM CEST #

First of all, thanks for a very useful blog post.

I ran a slightly modified version of your code but found one further (and, alas, significant) difference: The out-of-the-box server runs as a daemon, shutting down when the JVM does. It seems there is no way to force the JmxConnectorServer above to do so.

Posted by Niels Christensen on September 14, 2009 at 10:17 AM CEST #

Thanks for spelling out this information- it was definitely worth the read. You sound like you have spent your fair share of time with <a href="http://www.proxynetworks.com"> Remote Access</a> software like the JMX Management System. What others have you tried, and what did you think of them?

Posted by Ted on November 13, 2009 at 03:38 PM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
About

lmalvent

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today