Skip to main content

What is an MXBean?

Posted by emcmanus on February 13, 2006 at 9:58 AM PST

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 src="http://weblogs.java.net/blog/emcmanus/archive/memoryusage.gif"
width="352" height="208" />

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, and
committed will all be less than the value of
max.

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. href="http://download.java.net/jdk6/doc/api/javax/management/MBeanServerInvocationHandler.html#newProxyInstance(javax.management.MBeanServerConnection,%20javax.management.ObjectName,%20java.lang.Class,%20boolean)">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 using
getAttributes instead of
getAttribute:

    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 of
init, used, committed,
and max were itself a bundle of statistical
values, say min, current, max,
and average:

The values in a MemoryUsage might themselves be complex src="http://weblogs.java.net/blog/emcmanus/archive/memoryusagestats.gif"
width="712" height="232" />

Would you want to define 16 attributes, such as
UsedMin, 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 calling
getAttribute. You can have more than one attribute
that returns the bundle type, for example
getHeapMemoryUsage and
getNonHeapMemoryUsage.

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 src="http://weblogs.java.net/blog/emcmanus/archive/jconsoleunavail.gif"
width="548" height="255" />

The MemoryUsage attribute is shown as style="color: red; font-family: sans-serif">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 href="http://research.sun.com/techrep/2006/abstract-149.html">recent
paper by href="http://research.sun.com/people/mybio.php?uid=85242">Michael
Warres offers an excellent analysis of some of the
problems you can encounter with code downloading.

MXBeans

MXBeans are an extension of the MemoryUsage
approach we just saw, but designed in a way that avoids the
problem with classes. MXBeans were originally designed (by href="http://weblogs.java.net/blog/mandychung/">Mandy
Chung, Sanjay Radia, and me) as part of the work for href="http://jcp.org/en/jsr/detail?id=174">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 href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/management/package-summary.html">
java.lang.management in J2SE 5.0 or later. In
particular, the MemoryUsage example above is
inspired by the href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/management/MemoryUsage.html">MemoryUsage
class from that package, and the href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/management/MemoryMXBean.html">MemoryMXBean
interface that uses it.

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

JConsole showing MemoryUsage as a javax.management.openmbean.CompositeDataSupport src="http://weblogs.java.net/blog/emcmanus/archive/jconsolecompositeclosed.gif"
width="686" height="276" />

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

JConsole showing MemoryUsage as CompositeData items src="http://weblogs.java.net/blog/emcmanus/archive/jconsolecompositeopen.gif"
width="686" height="372" />

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 MBean MXBean
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 MemoryMBean
interface has been renamed to MemoryMXBean!

Recall that, with a Standard MBean, you define an interface
called SomethingMBean and a class called
Something that implements it. So in the
Standard MBean example here we have an interface
MemoryMBean 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 href="http://mustang.dev.java.net">mustang.dev.java.net.

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

    MBeanServer mbs =     href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/management/ManagementFactory.html#getPlatformMBeanServer()">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 as
MemoryUsage 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 href="http://download.java.net/jdk6/doc/api/javax/management/openmbean/package-summary.html">
javax.management.openmbean. The exact
mapping rules appear in the href="http://download.java.net/jdk6/doc/api/javax/management/MXBean.html##MXBean-spec">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 href="http://download.java.net/jdk6/doc/api/javax/management/openmbean/CompositeDataSupport.html">
CompositeDataSupport. This is why we saw the
MemoryUsage attribute as a
CompositeDataSupport 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 the
used item from the MemoryUsage bundle is
different depending on whether it's in a Standard MBean or an
MXBean:

Standard MBean MXBean
    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 interface
CompositeData rather than the specific class
MemoryUsage. 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 a proxy and then the Standard MBean and
MXBean cases are once again very similar:

Standard MBean MXBean
    MBeanServer mbs = ...whatever...;
    ObjectName name =
        new ObjectName("com.example:type=Memory");
    MemoryMBean proxy =
    JMX.    href="http://download.java.net/jdk6/doc/api/javax/management/JMX.html#newMBeanProxy(javax.management.MBeanServerConnection,%20javax.management.ObjectName,%20java.lang.Class)">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.    href="http://download.java.net/jdk6/doc/api/javax/management/JMX.html#newMXBeanProxy(javax.management.MBeanServerConnection,%20javax.management.ObjectName,%20java.lang.Class)">newMXBeanProxy(mbs, name, MemoryMXBean.class);
    MemoryUsage memoryUsage = proxy.getMemoryUsage();
    long used = memoryUsage.getUsed();

(Notice in passing that the hideously unmemorable
MBeanServerInvocationHandler.newProxyInstance has
been supplemented in Mustang by the much nicer
JMX.newMBeanProxy.)

An MXBean proxy is able to reverse the mapping that the MXBean
did. So the MXBean mapped from MemoryUsage to
CompositeDataSupport, and the proxy maps back from
this CompositeDataSupport to the original
MemoryUsage 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 this
MemoryUsage. The gory details are in the href="http://download.java.net/jdk6/doc/api/javax/management/MXBean.html##MXBean-spec">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 the
serialVersionUID definition from the definition of
MemoryUsage, 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 like com.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
be String and other times Integer or
Date. 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
.

Related Topics >>