Blog Control to Major Tom.
"there's still lofty dreams, meager desires, still silliness."
SPONSORED BY:
  Your Company Here  
Inquire Within

Reflection solves a JMX problem.

One of the cool things in the Java Platform I've always liked is the introspection and reflection capabilities. Although Java is surely not the first to provide this, it is quite a step up from using say, dlopen() and dlsym() in C code for example.

These features recently help me elegantly solve a problem I had with JMX. I have been looking at instrumenting the code that is used serve many of the www.sun.com web pages, in order to give us better information on how the system is performing.

The Java Management Extensions (JMX) provide a pretty straight forward way to enable management and instrumention in your applications. But here's the problem I was having.

The easiest way to use JMX is to use Standard MBeans. This is done by creating an bean interface class which you then implement in your code. The interface defines getter and setter methods for data points you want to monitor and/or change. JMX, itself, then uses introspection to find and use the MBean interface. Here's an example:

public interface DispatcherMBean {

	public int getRequestsProcessedCount();

	public void setCacheSize(int cacheSize);
}

public class Dispatcher implements DispatcherMBean {

	public int getRequestsProcessedCount() {
		return statRequestsProcessedCount;
	}

	public void setCacheSize(int cacheSize) {
		this.maxCacheSize = cacheSize;
	}

        /*
         * ...
	 */
}

Now to register the object with the MBeanServer its only a few simple method calls:

	ObjectName dispatcherName = null;
	dispatcherName = new ObjectName("Domain:name=Dispatcher");
	server.registerMBean(this, dispatcherName);

This all works great. Very straightforward. Very simple. The only problem is that although JMX provides the ability to tie descriptions to each attribute, the Standard MBean used this way doesn't provide the ability to create them. Having descriptions available would definitely be helpful when working in a Management Console trying to figure out what each attribute means.

So this is the problem I was trying to solve in the first place. How to easily maintain descriptions for the JMX attributes I was creating.

First, I looked into the other JMX MBeans. The Dynamic MBean, for example, provides alot more flexibility, including creating descriptions, but that flexibility comes at a cost. There is alot more code involved in instrumenting your code with Dynamic MBeans. You have maintain a lot of different structures like MBeanAttributeInfo, MBeanOperationInfo, MBeanConstructorInfo, etc.. You also have more generalized methods that you implement like getAttribute() and setAttribute(). The code becomes quite long, an example that comes with JMX is SimpleDynamic.java

So I decided that was out.

As I was trying out different JMX implementations, I noticed in the MX4J documentation that they provide an extension in MX4J for maintaining descriptions with Standard MBeans. The documentation says that this extension is "Totally transparent with respect to MBeans portability across JMX implementations." owever, the solution relies on a couple of MX4J-specific classes. Without them, your code would not compile, so in essence you are tied to MX4J. I strive to stay free from specific implementations so I decide this extension was out.

So as I searched around a little bit more, I found that there is an alternate way to create a Standard MBean which provides some customization hooks including the ability to maintain descriptions for attributes. Its done by using the base class StandardMBean. And the specific method that I was interested in was:

	public String getDescription(MBeanAttributeInfo info);

Ok, so I had to first restructure my code to extend StandardMBean, which has its own problems because much of our code already extends other classes. But I managed to come up with a peer class framework. The peer class is given access to protected fields for management, this ends up being a bit of a plus to have the management functions separated out.

Now the only problem is how to easily manage descriptions and how to implement getDescription(). One obvious thought is to create a Map of some sort or a Properties file ResourceBundle and initialize it with all the attribute names and descriptions. I started going down that route but it just seemed clunky.

As I looked back, and thought about how JMX uses reflection, I realized I could use reflection myself to easily accomplish this. What I figured I could do is come up with some sort of naming scheme to some internal String fields which I could programmatically search for and get the values of. And that's exactly what I did.

All that I do is create public static String fields that are named the same as the attributes with the additional suffix of Desc. And then I created a getDescription() method that takes the name of the attribute passed in, tacks on "Desc" to the end, and then uses Class.getField() to search for it.

My original example, modified this way looks like this:

public interface DispatcherMBean {
	public int getRequestsProcessedCount();

	public void setCacheSize(int cacheSize);
}

public class DispatcherMBeanImpl extends StandardMBean 
	implements DispatcherMBean {


	public DispatchMBeanImpl(Dispatcher dispatcher) {
		this.dispatcher = dispatcher;
	}

	public static String = 
		"Contains the total number of requests processed";

	public int getRequestsProcessedCount() {
		return dispatcher.statRequestsProcessedCount;
	}

	public static String =
		"The cache size, in megabytes.";

	public void setCacheSize(int cacheSize) {
		dispatcher.maxCacheSize = cacheSize;

	public String getDescription(MBeanAttributeInfo info) {
		try {
			Field field = getClass().getField(info.getName() + "Desc");
			return (String) field.get(this);
		}
		catch (Exception e) {
			e.printStackTrace();
			return info.getDescription();
		}
	}

       /*
        * ...
	*/
}
And to register the MBean, only a couple of simple lines still:
	DispatchMBeanImpl mbean = new DispatcherMBeanImpl(dispatcher);
	ObjectName dispatcherName = null;
	dispatcherName = new ObjectName("Domain:name=Dispatcher");
	server.registerMBean(mbean, dispatcherName);

And that's it. Pretty simple.


(2004-10-25 10:49:02.0) Comments [1] // Permalink

Comments:

If you're on Tiger, you could use annotations instead of static fields. For example:
public interface DispatcherMBean {
        @Description("Total number of requests")
	public int getRequestsProcessedCount();

        @Description("Cache size in megabytes")
	public void setCacheSize(int cacheSize);
}
We'll probably standardize this in JSR 255 (JMX API v2.0).

Posted by Eamonn McManus on October 26, 2004 at 09:30 AM PDT #

Post a Comment:

Comments are closed for this entry.

archives
links