MXBeans include a way to handle inter-MBean references conveniently. You can use this to build an MBean hierarchy that is simple to navigate.

In a previous blog entry, I described MXBeans. User-defined MXBeans are a new feature in the Mustang (Java SE 6) platform. They define a type mapping, so you can use arbitrary types in the Java interface that defines your management interface, but have them mapped to a fixed set of predefined types. More details in that earlier entry.

One thing I didn't mention there is that MXBeans contain a facility for managing references between MXBeans very simply. (This facility was based on an idea by Charles Paclat of BEA.) This is probably easiest to explain by example. Suppose I have someproducts, and each product is composed of one or moremodules. I have one MBean per product and one MBean per module. Given the MBean for a product, I would like to be able to see the MBeans for its modules, and vice versa. It looks something like this:

Product MBean referencing two Module MBeans 

The corresponding MXBean interfaces might look like this:

public interface ProductMXBean {
    public Set<ModuleMXBean> getModules();
    public void addModule(ModuleMXBean module);
    public void removeModule(ModuleMXBean module);
    public String getName();
    // ...
}

public interface ModuleMXBean {
    public ProductMXBean getProduct();
    public String getName();
    // ...
}

The ModuleMXBean has an attribute calledProduct, defined by the getProduct()method. The declared type of that attribute isProductMXBean, but this type will be mapped by the MXBean framework. So the actual type that a client such as jconsole will see will in fact be ObjectName. You might see something like this if you connected jconsole to this app:

JConsole showing a Module MXBean with an ObjectName in its Product attribute 

The Product attribute is anObjectName, specifically the name of theProductMXBean.

Creating references

What might the code that created the ProductMXBeanand ModuleMXBeans look like? Here's one possibility:

        // Create and register the Product
        ObjectName productName = new ObjectName("com.example.myapp:type=Product");
        ProductMXBean product = new ProductImpl("wonderprod");
        mbeanServer.registerMBean(product, productName);
        
        // Create and register the Modules and add each one to the Product
        String[] moduleIds = {"fred", "jim", "sheila"};
        for (String moduleId : moduleIds) {
            ModuleMXBean module = new ModuleImpl(moduleId, product);
            ObjectName moduleName =
                new ObjectName("com.example.myapp:type=Module,name=" + moduleId);
            mbeanServer.registerMBean(module, moduleName);
            product.addModule(module);
        }

Here we are creating the Product and its Modules all at the same time, so we are able to give the Product a direct reference to each of the Module objects and vice versa. The code in bold shows this happening. This approach assumes an intimate relationship between all the objects in question, and is most suitable if the code for creating all the MXBeans is small and centralized.

Another possibility is illustrated by rewriting the code as follows. Only the code in bold below has changed.

        // Create and register the Product
        ObjectName productName = new ObjectName("com.example.myapp:type=Product");
        ProductMXBean product = new ProductImpl("wonderprod");
        mbeanServer.registerMBean(product, productName);
        ProductMXBean productProxy =
            JMX.newMXBeanProxy(mbeanServer, productName, ProductMXBean.class);
        
        // Create and register the Modules and add each one to the Product
        String[] moduleIds = {"fred", "jim", "sheila"};
        for (String moduleId : moduleIds) {
            ModuleMXBean module = new ModuleImpl(moduleId, productProxy);
            ObjectName moduleName =
                new ObjectName("com.example.myapp:type=Module,name=" + moduleId);
            mbeanServer.registerMBean(module, moduleName);
            ModuleMXBean moduleProxy =
                JMX.newMXBeanProxy(mbeanServer, moduleName, ModuleMXBean.class);
            product.addModule(moduleProxy);
        }

The coupling between the Product and itsModules is much looser because each only knows theObjectName of the other, and not the object itself as before. The Modules could have been created in one place in the code and the Product somewhere else entirely, and they would only have had to agree on theObjectNames.

Navigating the MXBean hierarchy

The real power of this approach comes when you are coding a client that interacts with this model.

You can construct a proxy for an MXBean using the new method JMX.newMXBeanProxy. If you have a proxy for aModuleMXBean, say, then it is an object of typeModuleMXBean. If you call its getName()method, that will result in a call to MBeanServerConnection.getAttribute(moduleObjectName, "Name"). Very handy.

But the ModuleMXBean interface also has this method:
ProductMXBean getProduct();
What does it return when you call it on aModuleMXBean proxy?

You might have guessed the answer. It returns another proxy, this time for the ProductMXBean.

Suppose we known the ObjectName of aModuleMXBean, and we want to find the names of all the modules in the same product. We can do this by starting with a proxy for the ModuleMXBean and using it to obtain a proxy for its ProductMXBean:

Navigating from the ModuleMXBean to its ProductMXBean 

Then we can navigate from this proxy to proxies for all of the product's modules:

Navigating from the ProductMXBean to its ModuleMXBeans 

In code, finding the names of all the modules given just theObjectName of one module looks like this:

        ModuleMXBean startModuleProxy =
            JMX.newMXBeanProxy(mbeanServerConnection, startModuleName,
                               ModuleMXBean.class);
        ProductMXBean containingProduct = startModuleProxy.getProduct();
        Set<ModuleMXBean> modules = containingProduct.getModules();
        for (ModuleMXBean module : modules)
            System.out.println(module.getName());

The ability to navigate through the MBean model using proxies is a very powerful one, and a strong incentive to use this approach to managing references between MBeans.