Assign an automatic JMX port to a Java application

VisualVM and jconsole are two useful tools for debugging JVM issues. However they both rely on a JMX port to be open on the remote instance. You can work around this on the fly by running jstatd on the remote host, but you’ll find certain things disabled. So ideally, the jmx port will be enabled on startup.

This can be accomplished by doing something like this:

$ java  -Dcom.sun.management.jmxremote \
        -Dcom.sun.management.jmxremote.port=1080 \
        -Dcom.sun.management.jmxremote.local.only=false \
        -Dcom.sun.management.jmxremote.authenticate=false \
        -Dcom.sun.management.jmxremote.ssl=false \
        -jar MyApp.jar

Then connecting via jmx to port 1080 on the remote host.

Automated deployments

The problem with this approach becomes quickly apparent in an automated environment. If two applications are deployed on the same machine and they both try to open a 1080 port, one of them will fail. This conflict can also arise from non-obvious services, like running an ActiveMQ broker which by default opens a 1080 port.

The obvious next step is to try and assign a unique jmx port to each instance by hand. This is doable, but can quickly become a mess unless you develop some sort of jmx port registry system.

Fortunately there is a better way.

Ephemeral ports

Hotspot and openjdk have had an undocumented feature where setting com.sun.management.jmxremote.port=0 will cause the jvm to ask the OS to assign an ephemeral port. This will guarantee an unused port.

This has been around for years, so I don’t expect it to change. But being undocumented, you never know.

What port?

If you’re familiar with the usual unix tools, you should know that you can use something like lsof to figure out the port being used:

$ jps -l # figure out pid
$ lsof -p <pid> -n -i

Another approach that is more script / human friendly is to enable logging of the jmx server component. Once enabled it will print out the jmx port on startup to standard out. This can be done with a simple java.util.logging.config.file:

# logging.properties
handlers= java.util.logging.ConsoleHandler
.level= INFO
java.util.logging.ConsoleHandler.level = CONFIG
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
sun.management.jmxremote.level = CONFIG

Then modify the command line to specify the properties file and set the jmx port to 0:

$ java  -Djava.util.logging.config.file=logging.properties
        -Dcom.sun.management.jmxremote \
        -Dcom.sun.management.jmxremote.port=0 \
        -Dcom.sun.management.jmxremote.local.only=false \
        -Dcom.sun.management.jmxremote.authenticate=false \
        -Dcom.sun.management.jmxremote.ssl=false \
        -jar MyApp.jar

You should see the jmx port printed at startup as a CONFIG message:

CONFIG: JMX Connector ready at:
service:jmx:rmi:///jndi/rmi://disaster:39148/jmxrmi

Here the process got assigned 39148. This can be further processed by scripts to display the port on a webpage for quick access.

One thought on “Assign an automatic JMX port to a Java application

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s