One of the important new features of the JMX API in Mustang (Java SE 6) is the ability to create MXBeans. MXBeans provide a convenient way to bundle related values together without requiring clients to be specially configured to handle the bundles. Here's the complete story about MXBeans.

Bundles of values

When you are designing an MBean, you sometimes want to have a bundle of associated values. For example, you might have a bundle that represents a snapshot of memory usage. It might look something like this:

A MemoryUsage is a bundle of named values 

Typically a snapshot will have some implicit constraints based on the meanings of the values. In this case, for instance, the values of init, used, andcommitted will all be less than the value ofmax.

Each value as a separate attribute

One way you might model this is to have each value be an attribute of your MBean, like this:

   
public interface MemoryMBean {
    public long getInit();
    public long getUsed();
    public long getCommitted();
    public long getMax();
}

public class Memory implements MemoryMBean {
    ...
    public long getUsed() {
        return currentUsed();
    }
    private static native long currentUsed();
    ...
}

The disadvantage of this approach is that you could potentially get an inconsistent set of values. For example, if your client code looks like this...

   
    MBeanServerConnection mbsc = ...something...;

    // either call getAttribute directly...
    long max = (Long) mbsc.getAttribute(memoryMBeanName, "Max");
    long used = (Long) mbsc.getAttribute(memoryMBeanName, "Used");

    // ...or make a proxy...
    MemoryMBean memory = (MemoryMBean)
    MBeanServerInvocationHandler.newProxyInstance(mbsc, memoryMBeanName,
                                                      MemoryMBean.class, false);
    long max = memory.getMax();
    long used = memory.getUsed();

...then you might get a value of used that is greater than the value of max, even though that makes no sense. If you went back and got max again, the new value would indeed be greater than the used value.

You might think you could work around this problem by usinggetAttributes instead ofgetAttribute:

   
    MBeanServerConnection mbsc = ...something...;
    AttributeList attrs =
        mbsc.getAttributes(memoryMBeanName, new String[] {"Max", "Used"});
    if (attrs.size() != 2) throw new UnpleasantException("...");
    long max = (Long) ((Attribute) attrs.get(0)).getValue();
    long used = (Long) ((Attribute) attrs.get(1)).getValue();

This code is considerably nastier, and you don't have the option of making a proxy. It also doesn't solve the problem! Ultimately, the MBean Server will handle the getAttributes call by calling your MBean's getMax() method and then calling your MBean's getUsed() method. The interval between these calls might be smaller, but it's still possible for the values to be inconsistent.

You could solve this problem by making your MBean be a Dynamic MBean rather than a Standard MBean, and implementing DynamicMBean.getAttributes so that it does return a consistent set of attributes. That's a lot of work, though.

Furthermore, this idea of breaking a complex set of values out into separate attributes can quickly become unmanageable. Suppose that, instead of being plain long values, each ofinit, used, committed, andmax were itself a bundle of statistical values, saymin, current, max, andaverage:

The values in a MemoryUsage might themselves be complex 

Would you want to define 16 attributes, such asUsedMin, UsedCurrent,CommittedMin?

Defining a type for the bundle of values

An alternative approach is to define a Java class that represents the bundle of values you want to deal with, for example like this:

   
public class MemoryUsage implements Serializable {
    private static final long serialVersionUID = -3529132954339066973L;

    public MemoryUsage(long init, long used, long committed, long max) {
        this.init = init;
        ...
    }
    public long getInit() {
        return this.init;
    }
    public long getUsed() {...}
    public long getCommitted() {...}
    public long getMax() {...}
}

public interface MemoryMBean {
    public MemoryUsage getMemoryUsage();
}

public class Memory implements MemoryMBean {
    public MemoryUsage getMemoryUsage() {
        return memoryUsageSnapshot();
    }
    private static native MemoryUsage
            memoryUsageSnapshot();
    ...
}

Now your client code might look something like this:

   
    MBeanServerConnection mbsc = ...something...;
    MemoryUsage mu =
        (MemoryUsage) mbsc.getAttribute(memoryMBeanName, "MemoryUsage");
    long max = mu.getMax();
    long used = mu.getUsed();

This looks much nicer. The fact that the four values are related is explicitly reflected in the MBean's interface, and it's no longer possible for you to get an inconsistent set of values inadvertently. You can make a proxy rather than callinggetAttribute. You can have more than one attribute that returns the bundle type, for examplegetHeapMemoryUsage andgetNonHeapMemoryUsage.

But there's a subtle problem with this. For the client code to compile and run, we now need to supply it with a copy of the compiled MemoryUsage class. Suppose the client is a generic one like jconsole. If we connect jconsole to our MemoryMBean then by default we'll see something like this:

jconsole showing unavailable class MemoryUsage 

The MemoryUsage attribute is shown as Unavailable because jconsole doesn't know the class MemoryUsage. When it tries to retrieve the value of the attribute, it gets a ClassNotFoundException.

You can launch jconsole with a classpath that includes the MemoryUsage class. But this isn't a very good solution. JConsole can connect to any number of remote MBean Servers. Imagine if you had to figure out all the classes used by any of those MBean Servers and put them all in jconsole's classpath. Imagine if furthermore some of the MBean Servers used the same class but with different versions. This quickly gets messy.

Aside: Code downloading. A solution that is sometimes proposed to the classpath problems just mentioned is RMI code downloading. Jini technology uses this heavily, for example. The idea here would be that if jconsole can't find a class like MemoryUsage, it can download it from an HTTP server associated with the remote MBean Server. This is a nice idea if you can make it work. But you'll need to arrange for your HTTP server to have the right classes, and keep them in sync with the MBean Server, and configure any firewalls so that the HTTP connection is allowed through, and set up security in jconsole so that it doesn't allow the downloaded classes to do things they shouldn't, and use the RMI connector rather than any other connector. In a sense this is the ticket price for entering the Jini universe. Once you're in, there's plenty of great stuff. But if all you want to do is access bundles of values, the price is too high.

A recent paper by Michael Warres offers an excellent analysis of some of the problems you can encounter with code downloading.

MXBeans

MXBeans are an extension of the MemoryUsageapproach we just saw, but designed in a way that avoids the problem with classes. MXBeans were originally designed (by Mandy Chung, Sanjay Radia, and me) as part of the work for JSR 174, "Monitoring and Management Specification for the Java Virtual Machine". As such, you can see the use of MXBeans by looking at the package java.lang.management in J2SE 5.0 or later. In particular, the MemoryUsage example above is inspired by the MemoryUsage class from that package, and the MemoryMXBean interface that uses it.

Let's see how jconsole will work if we change theMemoryMBean into a MemoryMXBean:

JConsole showing MemoryUsage as a javax.management.openmbean.CompositeDataSupport 

That looks a bit cryptic, but in fact all we have to do is double-click on the javax.management.openmbean.CompositeDataSupportto get this:

JConsole showing MemoryUsage as CompositeData items 

The bundle of information we're interested in is there, and we didn't have to do any special configuration of jconsole to see it!

How do I write an MXBean?

To get this effect, we changed the Memory MBean from a Standard MBean to an MXBean. This involves almost no modifications:

         
Standard MBeanMXBean
public interface MemoryMBean {
    public MemoryUsage getUsage();
}
public class Memory implements MemoryMBean {
    public MemoryUsage getMemoryUsage() {
        return memoryUsageSnapshot();
    }
    private static native MemoryUsage
            memoryUsageSnapshot();
    ...
}
public interface MemoryMXBean {
    public MemoryUsage getUsage();
}
public class Memory implements MemoryMXBean {
    public MemoryUsage getMemoryUsage() {
        return memoryUsageSnapshot();
    }
    private static native MemoryUsage
            memoryUsageSnapshot();
    ...
}

The only change is that the MemoryMBeaninterface has been renamed to MemoryMXBean!

Recall that, with a Standard MBean, you define an interface called SomethingMBean and a class calledSomething that implements it. So in the Standard MBean example here we have an interfaceMemoryMBean and a class Memory that implements it.

With MXBeans, the convention is that the interface must be called SomethingMXBean. The class can be called Something as here, or in fact anything else. I usually use SomethingImpl. There is no requirement that the interface and the class be in the same Java package.

Registering an MXBean

Remember that you can only write MXBeans on Java SE 6 (Mustang). If you haven't already done so, you might want to download a snapshot from mustang.dev.java.net.

The code to register the MBean is exactly the same for a Standard MBean or an MXBean:

   
    MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
    // or any other MBeanServer

    ObjectName name = new ObjectName("com.example:type=Memory");
    Object mbean = new Memory();
    mbs.registerMBean(mbean, name);

How does an MXBean work?

The key idea behind MXBeans is that types such asMemoryUsage that are referenced in the MXBean interface get mapped into a standard set of types, the so-called Open Types that are defined in the package javax.management.openmbean. The exact mapping rules appear in the MXBean specification, but to oversimplify we could say that simple types like int or String are unchanged, while complex types like MemoryUsage get mapped to the standard type CompositeDataSupport. This is why we saw theMemoryUsage attribute as aCompositeDataSupport in jconsole above.

So if you run the following code you'll see the difference between a Standard MBean and an MXBean:

   
    MBeanServer mbs = ...whatever...;
    ObjectName name = new ObjectName("com.example:type=Memory");
    Object memoryUsage = mbs.getAttribute(name, "MemoryUsage");
    System.out.println(memoryUsage.getClass());

For a Standard MBean, this will print:
class com.example.MemoryUsage
whereas for an MXBean, it will print:
class javax.management.openmbean.CompositeDataSupport

So this means that the way you would extract theused item from the MemoryUsage bundle is different depending on whether it's in a Standard MBean or an MXBean:

         
Standard MBeanMXBean
    MBeanServer mbs = ...whatever...;
    ObjectName name =
        new ObjectName("com.example:type=Memory");
    MemoryUsage memoryUsage = (MemoryUsage)
        mbs.getAttribute(name, "MemoryUsage");
    long used = memoryUsage.getUsed();
    MBeanServer mbs = ...whatever...;
    ObjectName name =
        new ObjectName("com.example:type=Memory");
    CompositeData memoryUsage = (CompositeData)
        mbs.getAttribute(name, "MemoryUsage");
    long used = (Long) memoryUsage.get("Used");

Obviously this is a bit messier in the MXBean case, because we're dealing with the general-purpose interfaceCompositeData rather than the specific classMemoryUsage. But in fact if we are writing code that is specifically supposed to interface with the Memory MBean, as opposed to a generic client like jconsole, then we can construct aproxy and then the Standard MBean and MXBean cases are once again very similar:

         
Standard MBeanMXBean
    MBeanServer mbs = ...whatever...;
    ObjectName name =
        new ObjectName("com.example:type=Memory");
    MemoryMBean proxy =
        JMX.newMBeanProxy(mbs, name, MemoryMBean.class);
    MemoryUsage memoryUsage = proxy.getMemoryUsage();
    long used = memoryUsage.getUsed();
    MBeanServer mbs = ...whatever...;
    ObjectName name =
        new ObjectName("com.example:type=Memory");
    MemoryMXBean proxy =
        JMX.newMXBeanProxy(mbs, name, MemoryMXBean.class);
    MemoryUsage memoryUsage = proxy.getMemoryUsage();
    long used = memoryUsage.getUsed();

(Notice in passing that the hideously unmemorableMBeanServerInvocationHandler.newProxyInstance has been supplemented in Mustang by the much nicerJMX.newMBeanProxy.)

An MXBean proxy is able to reverse the mapping that the MXBean did. So the MXBean mapped from MemoryUsage toCompositeDataSupport, and the proxy maps back from this CompositeDataSupport to the originalMemoryUsage value. That's why the code to access a Standard MBean through a proxy is essentially the same as for an MXBean.

One subtle detail is that the MXBean framework needs some extra information in order to be able to reconstruct thisMemoryUsage. The gory details are in the MXBean spec, but the short summary is that you usually need to add an annotation to a public constructor of the class, that says how the parameters of the constructor match the properties. You usually only need to do this if you are accessing the MXBean through a proxy, but it's good practice to do it always. For example:

   
public class MemoryUsage {
    @ConstructorProperties({"init", "used", "committed", "max"})
    public MemoryUsage(long init, long used, long committed, long max) {
        this.init = init;
        ...
    }
    public long getInit() {
        return this.init;
    }
    public long getUsed() {...}
    public long getCommitted() {...}
    public long getMax() {...}
}

(I've also removed implements Serializable and theserialVersionUID definition from the definition ofMemoryUsage, because they're not necessary with MXBeans, though they do no harm.)

So when would I not write an MXBean?

Going forward, it will generally be a good idea to write an MXBean wherever you would write a Standard MBean today. The only real differences between MXBeans and Standard MBeans are these:

  • The naming convention is different. A Standard MBean is a class like com.example.Foo, that implements an interface like com.example.FooMBean. An MXBean is a class with any name you like, that implements an interface likecom.example.FooMXBean.
  • The types that you use in an MXBean interface get mapped into a predefined set of types. A consequence of this is that there are constraints on what types you can use. In my opinion this is a good thing because it makes for a much clearer type system.

In particular, one of the constraints on types in an MXBean interface is that you can't use inheritance. For example, you can't use Object as a wildcard type, that might sometimes beString and other times Integer orDate. If you know the complete set of possible types, you could do what JAXB does for an XML <choice>, namely define a set of properties, all but one of which are null.

Other than possible problems with inheritance, the other significant reason you might have for avoiding MXBeans is if your code needs to run on J2SE 5.0 or earlier. Obviously that reason will be less and less important as time goes on!

Update: see also my later blog entry on Inter-MXBean References.