Daniel Fuchs blogs on Scene Builder, JMX, SNMP, Java, etc...

javax.management.StandardMBean: When and Why.

Q: When is a Standard MBean not a Standard MBean?
A: When it's a StandardMBean.

A few month ago, we have added an Advanced JMX Example
to JDK 6. This example
builds up on the JMX Best Practices and discusses some usual JMX patterns
and pitfalls to avoid when designing a Management Interface with JMX.

In this article I will give a few additional tips concerning the StandardMBean class, and in particular, when and why it becomes
interesting to use it.

Standard MBeans are the simplest type of MBeans. They're described in the
JMX tutorial trail here.

Standard MBeans must have a management interface that follows strict
naming conventions: to be a compliant MBean, a standard MBean
implementation class named
<package-name>.Y must implement
an interface named <package-name>.YMBean - or must extend a class which does
follow these conventions.

Sometimes however, you'd like to be able to put interfaces and
implementations in different packages, so that you could deploy only
the interfaces on the client side. Interfaces are interesting to have
on the client side because they allow you to
create MBean proxies, which can simplify the client code. In the
general case however, you don't need to have also the implementation
classes on the client side, and it might even not be desirable
to deploy those on the client end.

For such use cases, being forced to put the MBean interface
YMBean in
the same package than the MBean implementation -
Y may become
quite constraining.

If you're running on the greatest JDK release ever aka JDK 6 ;-), you can
turn your MBean into an MXBean. For that, you only need to end your interface name with
MXBean. It does no longer need to remain
in the same package than the class that implements it.

---------------------------------------------------------------------------
// Interface can be in any package.
package <interface-package-name>;
// Must end with MXBean - or must use @MXBean annotations.
// Y doesn't need to be related to the implementation class
public interface YMXBean {...}
---------------------------------------------------------------------------
package <package-name>;
import <interface-package-name>.YMXBean;
public class YImpl implements YMXBean {...}
public static void main(String[] args) ... {
final MBeanServer server = ...;
final ObjectName name = ...;
final YImpl mbean = new YImpl(...);
// mbean is an MXBean.
server.registerMBean(mbean,name);
}
---------------------------------------------------------------------------

There are however some cases where using an MXBean is not feasible - for
instance, if your MBean uses types which are not supported by the
MXBean framework (for instance some methods use Object as return typess
or parameter types).

If you are working with an earlier Java release than JDK 6, or if your
MBean can't be turned into an MXBean, then you have another possibility: you
can wrap your MBean in a StandardMBean.

The javax.management.StandardMBean class will let you
turn your standard MBean into a DynamicMBean, and select its Management Interface. The interface must
be implemented by the implementation class, but its name doesn't need to
follow any naming convention. It simply needs to be a valid MBean interface.

---------------------------------------------------------------------------
// Interface can be in any package.
package <interface-package-name>;
// No name convention required! You can keep the name YMBean, or change it
// to whatever you like, like YInterface
public interface YInterface {...}
---------------------------------------------------------------------------
package <package-name>;
import <interface-package-name>.YInterface;
public class YImpl implements YInterface {...}
public static void main(String[] args) {
final MBeanServer server = ...;
final ObjectName name = ...;
final YImpl impl = new YImpl(...);
final StandardMBean mbean = new StandardMBean(impl,YInterface.class);
// mbean is an instance of StandardMBean: it's a DynamicMBean.
server.registerMBean(mbean,name);
}
---------------------------------------------------------------------------

Interestingly, you can also use StandardMBean to wrap an MXBean. Since MXBeans do not have any
constraints with regards to their interface names, this use case is
obviously different. We will see in the next section some of the odd cases
that might incite you into wrapping an MXBean inside a StandardMBean.

The laws by which the MBeanServer decides whether an MBean is a
DynamicMBean, a standard
MBean or an MXBean may appear to have some strange side effects.
Let's see how the MBeanServer discriminates between these three kinds
of MBeans:

First, the MBeanServer
looks whether the provided MBean implements the DynamicMBean interface. If so, it's a Dynamic MBean, end of story.
Then the MBeanServer determines whether the provided MBean can be a
standard MBean. If it does, or if one of its superclass does, then it's a standard MBean.
Lastly, if the MBean is neither a DynamicMBean nor a standard MBean, the
MBeanServer looks whether it could be an MXBean.
If neither case apply, the MBeanServer will throw a NotCompliantMBeanException.

Two is an MBean because it implements TwoMBean. The pair
(class example.Two, interface example.TwoMBean) thus satisfies the
standard MBean pattern.

TwoAndHalf is not a standard MBean because it doesn't implement
any interface called TwoAndHalfMBean, and none of its superclass
is a standard MBean. However, TwoAndHalf implements
ThreeMXBean.
Therefore it's an MXBean.

Three is a standard MBean because one of its superclass (Two)
is itself an MBean.

ThreeAndHalf is not a standard MBean because it doesn't implement
any interface called ThreeAndHalfMBean, and none of its superclass
is a standard MBean. However, ThreeAndHalf implements
ThreeMXBean.
Therefore it's an MXBean.

Finally Four is an MBean because it implements FourMBean.
The pair (class example.Four, interface example.FourMBean) thus
satisfies the standard MBean pattern.

This is where the class StandardMBean becomes interesting.
Since StandardMBean removes all
constraints on the naming of standard MBean interfaces, then StandardMBean
also lets you decide whether to wrap your MBean as a standard MBean, or as
an MXBean. Indeed StandardMBean has a constructor that will let you pass an
isMxBean flag which tells whether the management interface you're
selecting should be interpreted as a standard MBean interface or as an
MXBean interface.

final Three three = new Three(...);
final StandardMBean mbean =
new StandardMBean(three,ThreeMXBean.class,true);
// The MBean registered under 'name1' is a standard MBean.
// Its interface is TwoMBean.
server.registerMBean(three,name1);
// The MBean registered under name2 is an MXBean.
// It's interface is ThreeMXBean
server.registerMBean(mbean,name2);

The use case presented above is however a corner case, and it's very
unlikely that you will ever need to use StandardMBean in this way. The next section will discuss some
more interesting usages of the StandardMBean class, which apply both
to wrapped MBean and wrapped MXBeans.

The principal use case for using StandardMBean, aside from selecting
explicitely the management interface that should be exposed,
is customization of the names and description exposed by the MBeanInfo.

If you are using the JMX plugin for NetBeans, and if you use the wizzard to create a JMX MBean that
extends StandardMBean,
then NetBeans will automatically generate the implementations of these hooks
for you, based on the various descriptions that you supply in the JMX MBean
creation wizzard.

A small variation of the above use case is discussed below.
Rather than having your MBean extend StandardMBean, you can also choose
to implement a subclass of StandardMBean dedicated
to your MBean interface:

However, creating a generic subclass of StandardMBean has its own limitations. Since you will need to
accomodate both MBeans which are NotificationEmitters and MBeans which are not, you will also
most probably need to create a similar subclass of StandardEmitterMBean too.
This point is exposed below.

An other common use case for using StandardMBean is when you want to
intercept all calls to a particular MBean in order to perform some
pre/post operations. For instance, let's assume that I want to log all
setAttribute actions performed on a particularly sensitive MBean.
One way to do that would be to wrap that MBean in a subclass of
StandardMBean, in which I would override the setAttribute method.

A side effect of needing two classes (StandardMBean, StandardEmitterMBean) depending on whether an MBean is a
NotificationEmitter or not, is that each time you find a use case
that requires to extend StandardMBean, you're probably going to need to extend StandardEmitterMBean as well. Eventually, this can lead to quite complex
combinatory situations. I would therefore advise to analyze the situation
carefully before deciding to provide a generic purpose subclass of
StandardMBean. In my next blog, I'll show how this pitfall can sometime
be avoided.

Another point worth noting is that StandardMBean does not forward callbacks
from the MBeanRegistration interface to its wrapped MBean implementation.
If your MBean implements MBeanRegistration, and if you wrap it in a StandardMBean - or in a
StandardEmitterMBean, its preRegister method etc... will not be called. There's already
RFE 6450834 logged for that - and it will hopefully be fixed
in JDK 7.

In the mean time you may want to use your own subclass of StandardMBean
if you need to work around that, or wait for my next blog, which will
provide a more generic solution.

You may not have noticed, but wrapping an MBean in a StandardMBean (using
new StandardMBean(...);) instead of registering it directly, has the
side effect of making all MBeanConstructorInfo disapear from its MBeanInfo. This is not a bug, but a conscious choice. The constructors
exposed are those of the wrapped implementation, and would not allow to
re-create an MBean wrapped in a StandardMBean instance.

The only case where constructors are kept is when you make your own MBean
extend StandardMBean, and when the wrapped implementation is this.
In that case, the StandardMBean is the MBean, and may therefore
be reconstructed using createMBean.

Finally, here is the most obscure point. Sometimes, a JMXConnectorServer
will need to figure out which ClassLoader to use when deserializing parameters
sent by a remote client. It usually calls MBeanServer.getClassLoaderFor() for that purpose. The MBeanServer
will return the class loader from which the MBean class was loaded.
So if your MBean is a Thing,
the MBeanServer will return Thing.getClass().getClassLoader(). However, if
your MBean was wrapped in a StandardMBean, what is returned is
StandardMBean.class.getClassLoader(), which happens to be null (on JDK 5
and later) since StandardMBean is in rt.jar. So if your MBean happens to
use custom types, is not an MXBean, and is not on the CLASSPATH, it usually
result in a ClassNotFoundException fired back to the client.

One way to work around this is usually to create an anonymous subclass
of StandardMBean when registering your MBean - e.g. something like:

// Creating an anonymous subclass works only if you create it from a class
// that was loaded by a ClassLoader that has access to your MBean class
// ('Thing' in this case).
// This is obviously the case in this snippet of code, because we were
// able to write: final Thing thing = new Thing(...);
//
final Thing thing = new Thing(...);
final StandardMBean mbean = new StandardMBean(thing, ThingMBean.class) { };
server.registerMBean(mbean,name);

Don't be too worried, it's unlikely that you will need to resort to such
tricks.

It would seem to me that making a trivial subclass - the { } bit - indicates a design defect in either MBeanServer, StandardMBean, or both. It should be possible, in the case of a StandardMBean, to automatically use the classloader of the wrapped instance's class.
As you pointed out to me, it's necessary to get the classLoader right in order for isInstanceOf queries to work, so it applies even if you're careful to have your management interface only consist of simple types.
Incidentally, it appears that the comment preview function on blogs.sun.com is vulnerable to a type 1 XSS attack. Whom should I tell about that?

Hi Martin,
Yes you are right, the {} bit is a workaround to a design flaw. Unfortunately design flaws happen ;-\\. Sometimes you can fix them, and sometimes you have to live with the workaround. In this particular case, could we fix it, or would we introduce an incompatibilty?
I am not sure what would be the best response...
My own idea is that we should extend the DynamicMBean interface (= introduce a subclass of DynamicMBean) which would have an isInstanceOf method. StandardMBean could implement this new interface. But it would still leave us with the possible incompatibility issue...
As to vulnerability of blogs.sun.com, you can send it to me (you know who I am and where I work ;-)), and I will forward...

The problem we had been facing was maintaining the documentation for MBeans in two places. Once in Javadoc of Mbean interface and once in java file (or resource file as you mentioned) that gives to jmx agents at runtime. To mitigate this, I have created a small doclet, which can generate this java file with all javadoc documentation automatically populated.

There shouldn't be much overhead: if you use a regular MBean (a standard MBean in 2 words), the MBeanServer will use reflection to invoke methods on your MBean. If you use a StandardMBean, it's the StandardMBean that will do the reflection.

In either case - it's pretty much the same code that will be triggered.