This post originated from an RSS feed registered with Java Buzz
by Marc Logemann.
Original Post: Simple MBeans, dynamic MBeans and Spring
Feed Title: Logemann Blog
Feed URL: http://www.logemann.org/blojsom/blog/default/?flavor=rss2
Feed Description: Marc's thoughts on Java and more
JMX is a great way to expose application behavior and state. Of course there is always the possibility to do this by yourself but why ignoring a great infrastructure when its available for free. Since i am using Spring nowadays, i will show you how to leverage the convenience classes from Spring.
Lets assume we have a simple bean with some knowledge how to get relevant application settings. We could write a bean like this:
{code}
public class SettingsJMX {
ConfigurationManager configManager;
String logLevel;
int loggedOnUsers;
public void setConfigManager(ConfigurationManager configManager) {
this.configManager = configManager;
public String getLogLevel() {
return configManager.getConfigValue("logLevel");
}
public int getLoggedOnUsers() {
return configManager.getConfigValue("loggedOnUsers");
}
}
{code}
This is a bean which gets it service object "configManager" via Spring DI. For the sake of completeness, here is the Spring definition which is straighforward. As a sidenote, i dont visualize how the real configManagerService is defined.
{code}
{code}
To make this a MBean, we can use two great technologies: Spring and JDK5-annotations. If you also wanna go this way, just use the one-time plumbing for Spring as below:
{code}
{code}
Basically it tells Spring to use a MBeanExporter that looks for beans with annotations and exports them. What exactly is exported depends on the Assembler. In our case we naturally let the annotations do the real work of defining things. So the next step would be to annotate our Pojo outlined above.
{code}
@ManagedResource(objectName = "Netversys:name=Config",
description = "Configuration Manager")
public class SettingsJMX {
ConfigurationManager configManager;
String logLevel;
int loggedOnUsers;
public void setConfigManager(ConfigurationManager configManager) {
this.configManager = configManager;
}
@ManagedAttribute(description = "getLogLevel")
public String getLogLevel() {
return configManager.getConfigValue("logLevel");
}
@ManagedAttribute(description = "LoggedOnUsers")
public int getLoggedOnUsers() {
return configManager.getConfigValue("loggedOnUsers");
}
public String getSomethingNonJMX() {
return "foo";
}
}
{code}
There are more annotation attributes available but i want to make things simple for now. For each method we want to export, we have to put the "ManagedAttribute" annotation in front of it. "ManagedResource" defines how the JMX client will visualize the MBean node. In this example, the method getSomethingNonJMX() wont be exposed because of no annotation.
So whats more to do to let the JMX client see our MBean? Nothing. Thats the great thing with JDK5, the MBean server is shipped with the runtime. Just use the documented JMX flags when starting your application or server via java system properties. The properties could look like (unsecure in this case):
{code}
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
{code}
To see your exposed MBean, just call "jconsole" from the JDK5 or any other 3rd party JMX client.
But people could say that its quite unconvenient to write a getter for each attribute you want to monitor. What if configManager (or better your application) has hundreds of settings? You definitely dont want to write 100 getters and remember to write more every time you define a new application wide setting. Here you could use a Dynamic MBean which you have to write yourself. But no worry, registering that bean later on with Spring is equally easy, but the fact remains that the Spring exporter cant help you here.
A dynamic MBean has a more dynamic approach to describe what attributes should be visible. Hence the name dynamic right? So instead of relying on looking for getters, you can implement a method that returns the structure of the MBean, this structure is an object and can be programatically created. Lets see a simple example:
{code}
@ManagedResource(objectName = "Netversys:name=Settings", description = "Settings")
public class DynamicJMX extends AbstractDynamicMBean implements DynamicMBean {
ConfigurationManager configManager;
public void setConfigurationManagerService(ConfigurationManager configManager) {
this. configManager = configManager;
}
protected MBeanAttributeInfo[] createMBeanAttributeInfo() {
Map configMap = configManager.getConfiguration();
if(configMap != null && configMap.size() > 0) {
MBeanAttributeInfo[] mBeanAttributeInfos = new MBeanAttributeInfo[configMap.size()];
Set strings = configMap.keySet();
int i = 0;
for (String key : strings) {
mBeanAttributeInfos[i] =
new MBeanAttributeInfo(key, String.class.getName(), key, true, true, false);
i++;
}
return mBeanAttributeInfos;
}
return null;
}
protected String getMBeanDescription() {
return "dynamic bean";
}
public Object getAttribute(String string)
throws AttributeNotFoundException, MBeanException,
ReflectionException {
return configManager.getConfigurationStringValue(string);
}
}
{code}
First lets grasp the things we know. There are annotations used by Spring as i mentioned already. We have our service injected via setter injection, so also nothing new here. The first main issue is that we extend AbstractDynamicMBean. To be honest, this is a convenience class from MX4J. Implementing DynamicMBean Interface is quite some work and with AbstractDynamicMBean, you only have to worry about the things you really want to implement.
In our case we just want to expose attributes. A lot of attributes. In my case there are about 50. The createMBeanAttributeInfo() method is most likely the most exciting one. The JMX client will call this method to get infos about the structure of this very MBean. In our case, the structure depends on the output of the configManager and its underlying Map datastore. I am storing all system settings in a Map that can be retrieved by calling configManager.getConfiguration(). Now for each key i am creating a structure object (MBeanAttribute) in the strucure array.
After that the client wants to display the values of each attribute by calling getAttribute(). Everything from here is more than easy as you can see. The configManager.getConfigurationStringValue(key) is mostly only a wrapper around Map.get(key) with some implicit type casting. I could imagine that exposing Maps as attributes is such a common task that there might be already solutions to this, but its not that hard to create your own little class for this as shown here.
The only thing in spring you have to do is the registration of the dynamic JMX bean:
{code}
{code}
The detection is done via the magic attribute autodetect = true, which we have seen at the top of the article.
All in all its quite easy to make this happen when you are on JDK5, because many things like annotations or the integrated JMX server shield you from the pain of starting a seperate jmx server and registering all the MBeans yourself. I really only covered a small fraction of what could be done but perhaps i motivated you to look more into JMX and see how it can be useful for your app.
Special thanks to {link: Brian Goetz|http://www.briangoetz.com/pubs.html} for {link:inspiring|http://www.ibm.com/developerworks/java/library/j-jtp09196/index.html} me once again to look into something new.