Skip to main content

Can you register the same MBean with more than one name?

Posted by emcmanus on July 16, 2005 at 7:03 AM PDT

The short answer is: you can, but you probably shouldn't.
Here's why.

To be clear, here's the sort of thing I'm talking about:

CacheControlMBean mbean = new CacheControl(thingyCache);
mbeanServer.registerMBean(mbean, objectName1);
mbeanServer.registerMBean(mbean, objectName2);

This will work, and the same MBean will be accessible through
both objectName1 and objectName2. So
why shouldn't you do that?

From a high level point of view, the immediate counterquestion
is, why would you want to? What does it mean for the
same object to be both

domain:type=CacheControl,name=Thingies and

domain:type=CacheControl,name=Whatsits ? When you
access the MBean, how is it supposed to know whether it is
acting as the Thingy cache controller or the Whatsit cache
controller?

Well, maybe Thingies and Whatsits are actually the same, but
that's an implementation detail that you don't want to expose.
Let's pretend we believe that and look at some lower-level
reasons why you still don't want to use the same MBean for both
of them.

MBeanRegistration

The first thing that ought to make you hesitate before
registering the same MBean twice is the

MBeanRegistration
interface. As a reminder,
MBeanRegistration looks like this:

package javax.management;
public interface MBeanRegistration {
    ObjectName preRegister(MBeanServer mbs, ObjectName name) throws Exception;
    void postRegister(Boolean done);
    void preDeregister() throws Exception;
    void postDeregister();
}

If an MBean implements this interface, then the various methods
will be called over the MBean's lifecycle: the first two before
and after registration, the last two before and after
unregistration. This allows an MBean to know what name it has
been registered under, and in what MBean Server.

But what happens if you register the same MBean with two
different names? (Or in more than one MBean Server?) Then the
preRegister method will be called once for each
name. The MBean had better be able to handle this.

It isn't impossible to deal with this, but it's hard. Rather
than just stashing the MBeanServer and
ObjectName in fields, the MBean is going to need to
maintain a Set<ObjectName> and maybe a
Set<MBeanServer> too. The parameter to the
postRegister method indicates whether registration
was successful: if it wasn't, how do we know which
ObjectName to remove from the Set? The
preDeregister and postDeregister methods
don't say what ObjectName is being unregistered, so
again how do we know which one to remove from the
Set?

You could probably solve this problem in most cases by checking
to see whether each ObjectName in the
Set is still there. For
postRegister(false) and
postDeregister(), you expect one of them to be
missing. You'd have to be very wary of race conditions, though.
And there is still no way of knowing in preDeregister
which ObjectName is about to be unregistered.

Notifications

If an MBean wants to emit notifications, it implements the

NotificationBroadcaster
interface (or better,
NotificationEmitter). Then, when it emits a

Notification
, the notification will be sent
to every listener that was added using

NotificationBroadcaster.addNotificationListener
.
But that method doesn't have a parameter to tell you which
ObjectName the client thinks it's adding its
listener to. So if the same MBean is acting as the Thingy cache
controller and the Whatsit cache controller, then listeners for
the Thingy cache controller are presumably going to get
notifications where the ObjectName returned by
getSource() is the Whatsit cache controller name,
and/or vice versa.

A further point is that the convenient ability to specify a
reference to the MBean as the notification source and have the
MBean Server replace it with the ObjectName obviously
doesn't work here. Relying on that source rewriting is
discouraged for other reasons, so if this were the only argument
against registering the same MBean more than once it wouldn't be
enough.

Inter-MXBean references

In Mustang (Java SE 6), the JMX API acquires user-defined

MXBeans
. (A fixed set of these already exists in

java.lang.management
in Tiger.) One of the
convenient features of MXBeans for developers is the ability to
code

inter-MXBean references
. An MXBean defines a getter for
another MXBean type, like this:

public interface ModuleMXBean {
    public ProductMXBean getProduct();
}

The MXBean mapping converts this into an attribute called
Product of type ObjectName. When you
call

getAttribute
, the ProductMXBean
reference returned by getProduct() is converted
into the ObjectName under which that MXBean is
registered. Obviously, for this to work that MXBean cannot
be registered under more than one name. In fact MXBeans
refuse to be registered under more than one name in the same MBean
Server for this reason.

There are many advantages to using MXBeans, so if you are
coding MBeans today you should at least be thinking about
migrating them to MXBeans in the future. If you register the
same MBean with more than one name then you will have problems
when you migrate.

So what should you do instead?

When you are tempted to register the same MBean under two
different names, most often what you really want is to
have the same "resource" that has two different management views.
In our example, the resource is the cache, which has one view that
sees it as a cache of Thingies, and another view that sees it as a
cache of Whatsits. The attributes and operations of these two
views might behave the same but chances are that that is more of a
coincidence than a fundamental property. As the code evolves,
sooner or later you'll want some attribute to be different in each
one. And as we've seen, notifications must say whether they come
from one or the other. If Thingies and Whatsits are secretly the
same, then when the Thingy cache overflows, the Whatsit cache will
also overflow. But if I've asked to receive a notification for
the former, I don't want to be told about the latter.

If you already have code that is registering the same MBean
twice, you may be able to make a quick fix as follows. Instead
of this...

CacheControlMBean mbean = new CacheControl(thingyCache);
mbeanServer.registerMBean(mbean, objectName1);
mbeanServer.registerMBean(mbean, objectName2);

...try this...

CacheControlMBean mbean = new CacheControl(thingyCache);
Object mbean1 = new StandardMBean(mbean, CacheControlMBean.class);
Object mbean2 = new StandardMBean(mbean, CacheControlMBean.class);
mbeanServer.registerMBean(mbean1, objectName1);
mbeanServer.registerMBean(mbean2, objectName2);

But ultimately, you'll just want to make two different MBeans
that reference the same underlying resource
(thingyCache here)...

CacheControlMBean thingyMBean = new CacheControl(thingyCache);
CacheControlMBean whatsitMBean = new CacheControl(thingyCache);
mbeanServer.registerMBean(thingyMBean, thingyObjectName);
mbeanServer.registerMBean(whatsitMBean, whatsitObjectName);

...which accurately reflects what you mean.


Model MBeans
may also come in useful here. But that's a
story for another day.

Related Topics >>