How to monitor and manage your Java application with JMX

JMX (Java Management Extensions) provides the infrastructure to support monitoring and management of your Java applications. Resources you manage with JMX are called Managed Beans (MBeans). I want to show you how to quickly register your own Service as MBean using Spring and Source-Level Metadata (JDK 5.0+ annotations).
The following sample is built on a tool that allows to manage the staffs’ applications for vacation digitally instead of using paper. If a staff member applies for leave, the application gets the status waiting. Then an authorized person (the boss) has to decide about this application. It may be set to allowed or to rejected. It might be that you want to have an overview of the applications and their status and you may even want to remind the authorized persons via email to review the pending applications. Even if the vacation management tool has a web-based frontend for doing the most of the actions, I think it still makes a good example for describing how to use JMX in your Java application. The following class is a skeleton of the class which shall be exposed to JMX as MBean.

public class JmxDemo {
    private long numberOfWaitingApplications;
    public long getNumberOfWaitingApplications() {
        return numberOfWaitingApplications;
    }
    public long countApplicationsInStatus(String status) {
        // do something and return number of applications with the given status
    }
    public List<String> showWaitingApplications() {
        // do something and return a list of all waiting applications
    }
    public String remindBossAboutWaitingApplications() {
        // remind the boss via email to decide about the waiting applications
    }
}

If you want to use this class as a MBean, a few steps are necessary.

1. Not yet another xml file…

It’s best you create an extra xml file (let’s call it jmxContext.xml) for JMX configuration and import it in your applicationContext.xml. In your jmxContext.xml you define your MBean and the MBeanExporter.

<bean id="jmxDemo" class="org.synyx.urlaubsverwaltung.jmx.JmxDemo">
	    <!-- maybe you need contructor-injection -->
	    <!-- <constructor-arg ref="myService" /> -->
</bean>
<!-- you may just copy the following lines -->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
        <property name="autodetect" value="true" />
        <property name="namingStrategy" ref="namingStrategy" />
        <property name="assembler" ref="assembler" />
</bean>
<bean id="jmxAttributeSource" class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" />
<bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
        <property name="attributeSource" ref="jmxAttributeSource" />
</bean>
<bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
        <property name="attributeSource" ref="jmxAttributeSource" />
</bean>

If your application is running inside a container such as Tomcat, you even don’t have to configure the MBeanServer because the container has its own one.
Setting MBeanExporter’s property autodetect to true, means that the MBeanExporter will register all the Beans within your application’s context that are annotated in the way described in the next section as MBeans.

2. Let’s transform your Spring Bean to a Managed Bean!

Spring uses information provided by annotations to generate MBeans. The attributes of the annotations are speaking for themselves so further description isn’t necessary. To mark a Bean for export, it has to be annotated with @ManagedResource, Attributes are annotated with @ManagedAttribute and Methods with @ManagedOperation.
2.1 Bean
Mark your Bean with @ManagedResource.

@ManagedResource(objectName = "mbeans:name=myJmxDemoBean", description = "My managed Bean.")
public class JmxDemo {
    // lot of stuff
}

Make sure that your MBean doesn’t contain ‘MBean’ in its name since it would be treated as a StandardMBean causing your annotations not to work.
2.2 Attributes
Annotate the Getter and Setter with @ManagedAttribute.

@ManagedAttribute(description = "Get the number of all waiting applications" )
public long getNumberOfWaitingApplications() {
    return numberOfWaitingApplications;
}

Exposing attributes may be:

  • Basic types
  • Primitives and their wrappers
  • String
  • BigDecimal
  • BigInteger
  • Date
  • Arrays and collections of basic types

2.2 Methods
Annotate each method you wish to expose with @ManagedOperation.

@ManagedOperation(description = "Shows a list of all waiting applications with some information.")
public List<String> showWaitingApplications() {
    // do something and return a list of all waiting applications
}

If your methods have parameters you can describe them further with @ManagedOperationParameters.

@ManagedOperation(description = "Get the number of all applications that have the given status.")
@ManagedOperationParameters({
    @ManagedOperationParameter(name = "status", description = "The status may be waiting, allowed, rejected or cancelled.")
})
public long countApplicationsInStatus(String state) {
    // do something and return number of applications with the given status
}

Make sure to annotate your Getter/Setter with @ManagedAttribute and not with @ManagedOperation. Otherwise your methods won’t work.

3. Try it!

You can now use the functions of your MBean either with JConsole or with other tools. (e.g. JMinix)
3.1 JConsole
JConsole is part of Oracle’s JDK, so you can just start it by executing the JConsole command in your JDK’s binary-folder. You can connect to local or to remote Java Virtual Machines. If you are running your application on the same host as JConsole it should show up at the ‘Local Process’ section.

3.2 JMinix
If you want to have a JMX entry point in your web application instead of using JConsole, JMinix might be the right choice for you.
JConsole
You can include it easily in your Maven based web application:
Add JMinix as dependency in your pom.xml

<dependency>
        <groupId>org.jminix</groupId>
        <artifactId>jminix</artifactId>
        <version>1.0.0</version>
</dependency>

JMinix uses a simple HttpServlet that you have to register and map to an url-pattern in your web.xml


<servlet>
        <servlet-name>JmxMiniConsoleServlet</servlet-name>
        <servlet-class>org.jminix.console.servlet.MiniConsoleServlet</servlet-class>
</servlet>
<servlet-mapping>
        <servlet-name>JmxMiniConsoleServlet</servlet-name>
        <url-pattern>/jmx/*</url-pattern>
</servlet-mapping>

4. Notifications

Notifications (javax.management.Notification) can be broadcast from your component to notify about something interesting happening. This is only a simple example of using Notifications.
Example: You want to be notified if a user logs in.

@ManagedResource(objectName = "mbeans:name=myJmxDemoBean", description = "Manage some 'Urlaubsverwaltung' problems.")
public class JmxDemoReady implements NotificationPublisherAware {
    // lot of stuff
    private NotificationPublisher notificationPublisher;
    public void notifyAboutLogin(String msg) {
        notificationPublisher.sendNotification(new Notification("Login Action", this, 0, msg));
    }
    @Override
    public void setNotificationPublisher(NotificationPublisher notificationPublisher) {
        this.notificationPublisher = notificationPublisher;
    }
}

With the NotificationPublisher you are able to create Notifications in a very simple way. At the right place in your code, you inject your JmxDemo Bean and call the method notifyAboutLogin() when a user logs in. JConsole now displays a third menu item called ‘Notifications’, besides ‘Attributes’ and ‘Operations’. If you click on ‘Subscribe’, you get a Notification every time a user logs in your web application.

5. Further information:

Spring Framework Reference Documentation
About JConsole
About JMinix
About the vacation management web application

Kommentare

  1. Thank you for this good remark! With the <code>context</code> namespace the registration of a MBean is even more easier - and kind of magic :-)

  2. Cool post! Have a look at the <code>context</code> namespace. It offers <code>mbean-server</code> and <code>mbean-exporter</code> elements to shrink down the rather verbose <code>bean</code> declarations. For more information have a look at the <a href="http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/jmx.html#jmx-context-mbeanexport" rel="nofollow">relevant section of the reference documentation</a>. Cheers, Ollie.