With a Standard MBean, you define the management interface of the MBean using a Java interface. Getters and setters in the interface define attributes, and other methods define operations. But the only information extracted out of the interface is the names and types of the attributes and operations, and just the types of the operation parameters. Although the JMX API allows for textual descriptions to be associated with attributes, operations, and parameters, when you use a Standard MBean these descriptions have meaningless default values. Parameters also have default names like p1, p2, etc. Here's how you can use subclassing and annotations to overcome these limitations.

Well, this is good, the operation is there, but the information about it could be improved. The useful parameter names we gave have been replaced with p1, p2, p3, and when we let the mouse hover over the operation button, we get a tooltip with the default text "Operation exposed for management".

You wouldn't have expected the MBean Server to have been able to guess a better description than that, but you might be surprised that the parameter names that were in your originalCacheControllerMBean interface have been lost. The reason for that is that the contents of the MBeanInfo for a Standard MBean are determined using reflection, and parameter names are not available through the reflection API. (The reason for that is that parameter names are not needed when compiling or executing method calls from other classes, so they don't appear in .class files, except when they're compiled with extra debug info. We wouldn't want the behaviour to be different when compiled with debug info or not, so no parameter names for us.)

With a bit more work you can put more information into your MBeans to produce a better user interface. The technique I'm suggesting is to add annotations to your Standard MBean that will allow you to specify descriptions for operations (and attributes and MBeans) and to give parameter names in a way thatis accessible through reflection. The idea is that your MBean interface would look like this:

When you're defining an annotation, you need to check out the meta-annotations in java.lang.annotation. (Meta-annotations are annotations for annotation definitions like the definition of@PName here.) They are:

You probably don't want this redundant information, although it might be useful to make sure the annotations are in fact present and have the right values.

Inherited applies to annotations on classes only, so it is not relevant to any annotations for Standard MBean interfaces.

Retention specifies whether the annotation is used only by tools (like the compiler) that read source code; or by tools that read class files; or by code that uses reflection. You might want to define some annotations that work in conjunction with the apttool, in which case all three retention values could be interesting. Usually, though, you will wantRetentionPolicy.RUNTIME.

Target specifies a list of places in the Java language syntax where the annotation can be used. This one is only appropriate as an annotation on parameters, so that's what you say.

By the way, it's important that the name of the method in the@PName annotation be value and not anything else. If it were called name, say, you would have to write @PName(name="n") rather than just@PName("n").

This should be fairly clear based on the discussion of the@PName annotation.

Now that you have these annotations, how do you arrange for them to be taken into account? The idea is to create a subclass of javax.management.StandardMBean that will add the information from the annotations to the information that is already deduced by the normal rules for Standard MBeans. TheStandardMBean class defines a number ofprotected methods that are specifically designed to be overridden for this sort of use. So you can define a class that looks like this:

By the way, an advantage of this approach is that you no longer have to follow the rigid naming convention where classcom.example.CacheController has to implement interfacecom.example.CacheControllerMBean.

Let's consider the @Description annotation for operations first. The method you want to override is this one:

protected String getDescription(MBeanOperationInfo op) {...}

You're going to want to find the method corresponding to this operation, and if it has a @Description annotation, return the value of the annotation as the description. Otherwise just return the default value ofop.getDescription().

Finding the method corresponding to anMBeanOperationInfo is not completely straightforward because the parameter types in the containedMBeanParameterInfo[] are expressed asString rather than Class. (This is because Class is not serializable.) EachString is the result of Class.getName() on the correspondingClass. You're going to need the originalClass objects so that you can call Class.getMethod to find the Methodobject and access its annotations.

Unfortunately, there is no standard method to convert back fromClass.getName() to the original Class. Class.forName comes close, but it doesn't do the right thing for primitive types like int.class (also known as Integer.TYPE). int.class.getName()returns "int", but if you give that toClass.forName it will look for a class calledint, which it is unlikely to find. So you'll need a helper method:

Getting the parameter names out of the @PNameannotation needs the same kind of code. You need to do a little more work, because there is no methodMethod.getParameterAnnotation that would allow you get the value of a particular annotation for a particular method parameter, the way you can with Method.getAnnotation. But it's easy enough to define. If you just wanted to write a method that returned the @PName annotation you would write this:

The fiddling with generics is to say that, if theannot parameter is PName.class, say, then the return type is PName. This saves the caller from having to cast. The standard methods such asMethod.getAnnotation do the same thing. Writing<A extends Annotation> means you will get a compiler error if you accidentally call the method with a class that is not an annotation.

Now you have everything you need for the override that extracts a parameter name from the @PName annotation:

Using the AnnotatedStandardMBean we've just defined, the jconsole window now looks like this:

The tooltip contains useful text, and the parameters have real names. How gratifying!

There are still some other things you're likely to want in theAnnotatedStandardMBean class. It should be possible to add a @Description annotation to a getter and/or setter method to define the description of an attribute. You might want some support for internationalizing descriptions, for example by including a resource key in the annotation. Operations have animpact field that you might want to be able to set via annotations. All of these things can be built rather simply based on the concepts here.