Using Spring JMX within Java Applications

0
104
6 min read

(For more resources on Java, see here.)

Yet for all its powerful capabilities, JMX is greatly underutilized and few developers seem to take advantage of its power. I attribute this underutilization to two factors: the scope of the Java universe as well as JMX’s complex development model. As a deep and wide universe composed of a seemingly infinite number of tools, frameworks, design patterns and a never ending stream of new thoughts and ideas, I believe that JMX rarely finds itself on the list of the next technologies a developer plans to explore. While other shiny objects steal the Java community spotlight, the benefits of JMX patiently wait to be discovered and seem to largely be the playing field of only seasoned Java veterans who have had the time or industry longevity to have already encountered it. In regard to its complex development model, JMX itself has an extremely low level, clumsy, and obtrusive API and that has directly hindered its adoption. While this complex development model is a fact of JMX life, the Spring framework, as with numerous other aspects of Java development, offers excellent JMX support that greatly simplifies and radically reduces the learning curve and time investment required to incorporate JXM into your application. Spring’s JMX support transforms JMX from an obscure API into what could become a central component of your application’s architecture.

While all of this sounds great, a tangible example of how easy it is to incorporate Spring JMX (and therefore JMX itself) into your application will make things more concrete. The following code and configuration sample presents a classic example of the benefits of JMX and is a piece of functionality which has proven its usefulness dozens upon dozens of times within my career: the ability to dynamically change an application’s Log4j log level at runtime.

Example 1:

package com.spiegssoftware.common.util.management.logging;

import org.apache.log4j.Category;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.jmx.export.annotation.ManagedOperation;

/**
* MBean exposing Log4j management operations.
* <p>
* This code is based on an example provided from http://uri.jteam.nl/?p=4 .
*/
public class Log4jJmxService
{
/** Logger for this class. */
private final Logger logger = Logger.getLogger(Log4jJmxService.class);

@ManagedOperation(description = “Set this Logger to the DEBUG level”)
public boolean activateDebug(final String category)
{
return adjustLogLevel(category, Level.DEBUG);
}

@ManagedOperation(description = “Set this Logger to the INFO level”)
public boolean activateInfo(final String category)
{
return adjustLogLevel(category, Level.INFO);
}

@ManagedOperation(description = “Set this Logger to the WARN level”)
public boolean activateWarn(final String category)
{
return adjustLogLevel(category, Level.WARN);
}

@ManagedOperation(description = “Set this Logger to the ERROR level”)
public boolean activateError(final String category)
{
return adjustLogLevel(category, Level.ERROR);
}

@ManagedOperation(description = “Set this Logger to the FATAL level”)
public boolean activateFatal(final String category)
{
return adjustLogLevel(category, Level.FATAL);
}

protected boolean adjustLogLevel(final String category, final Level level)
{
boolean result = false;

Category cat = LogManager.exists(category);

if (cat == null)
{
logger.error(“Logger ‘” + category + “‘ does not exist”);
}
else
{
logger.info(“Activating ” + level + ” for category: ” + category);
cat.setLevel(level);
result = true;
}

return result;
}
}

 

Example 2:

<?xml version=”1.0″ encoding=”UTF-8″?>
<beans

xsi_schemaLocation=”http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd”>

<bean id=”log4jJmxService” class=”com.spiegssoftware.common.util.management.logging.Log4jJmxService” />

<bean id=”exporter” class=”org.springframework.jmx.export.MBeanExporter”>
<property name=”beans”>
<util:map id=”beans”>
<entry key=”com.spiegssoftware.common.util.management.logging:name=Log4jJmxService” value=”log4jJmxService”/>
</util:map>
</property>
</bean>

</beans>

 

Based on code publicly available from Dev Thoughts, the class in Example 1 was decorated with Spring JMX annotations and the necessary Spring configuration was created in Example 2. To incorporate this functionality into your application, you will need to include this class (along with Spring JMX’s dependencies) and the configuration into your project, rebuild, and deploy. Before starting your application server, you may need to enable its support for JMX; see the documentation for your specific application server for details. After your application server has started with JMX support enabled, any JMX console, such as the jConsole tool that ships with all recent JDK’s, can be used to connect to the JVM the application is running within and the application’s logging level can be adjusted without requiring a restart.

The details of how to use jConsole are best left to its documentation, but for the impatient, jConsole can by be launched by opening a command window and issuing a “jconsole” command just as you would issue a “java -version”. From there, select which JVM you wish to connect to; most likely you will want to connect to a local process. After selecting the MBeans tab, use the left hand navigation and find the Log4jJmxService under the key name you registered it under within your Spring configuration file; in Example 2 we chose to use a value of “log4jJmxService”. After selecting the Log4jJmxService from the jConsole tree navigation and drilling down, you are presented with a screen that represents all of the public methods available on the Log4jJmxService. Simply clicking the invoke button next to each available public method results in the specified method on the Log4jJmxService being invoked just as if the bean’s method had been invoked through traditional application user input; the application is unaware and indifferent as to the source of the invocation request and the normal execution flow takes place.

You now have the ability to dynamically change the log level of your application at runtime. This JMX stuff is great, hu?

With your toe now in the JMX waters, you’re undoubtedly thinking of the numerous ways JMX can be incorporated within your applications: to inspect or alter application configuration, to access statistical data held within an application memory, or to manage an application by invoking application logic – all at runtime! JMX’s uses are limited only by your creativity to incorporate it.

JMX is so powerful and exposing your Spring based components through Spring JMX is so easy and convenient that it’s likely you’ll quickly find yourself wanting to expose every Spring bean throughout your entire application. While the two configuration strategies provided by Spring JMX (annotating classes or configuring beans in XML) are suitable for configuring a relatively low number of beans, when applied on a large scale each strategy has the disadvantage that it becomes tedious, verbose, and is the epitome of boilerplate; few would dispute that very quickly either your code or configuration becomes cluttered with JMX metadata. Having previously fallen into this advantageous trap of wanting to expose all Spring beans within an application multiple times before myself, it was time to take a step back and determine if this could accomplished in a better way.

LEAVE A REPLY

Please enter your comment!
Please enter your name here