Defining MBeans with annotations
The number one question I get about the JMX API at conferences
and other public events is whether there will be support for
defining MBeans using annotations. People see that they can
make EJBs or Web Services just by adding annotations to a
href="http://en.wikipedia.org/wiki/Plain_Old_Java_Object">POJO,
and they ask why they can't make MBeans the same way. In
version 2.0 of the JMX API, being defined by
href="http://jcp.org/en/jsr/detail?id=255">JSR 255, this
will be possible.
The exact details are still subject to change as a result of
discussions within the JSR 255 Expert Group, but here's a snapshot
of where we are now. I think the final version will be fairly
close to this.
In addition to defining MBeans with annotations, there are some
new proposed annotations that will also apply to MBeans defined
in the existing ways.
Prior art
Several projects already exist that provide this functionality,
but the most developed is probably Spring. So our starting point
was
href="http://static.springframework.org/spring/docs/2.0.x/reference/jmx.html#jmx-interface-annotations">Spring's
MBean annotations (see also
href="http://static.springframework.org/spring/docs/2.0.x/api/org/springframework/jmx/export/annotation/package-summary.html">API
documentation).
Defining an MBean
In the proposed design, an MBean can be defined through
annotations to achieve the same effect as a
href="http://java.sun.com/docs/books/tutorial/jmx/mbeans/standard.html">Standard
MBean. In this and other examples, I'll show what you write
with today's API, and what you'll be able to write with the new
annotations.
| Today | Tomorrow |
|---|---|
|
|
This defines an MBean with read-only attribute Used,
read-write attribute Size, and operation
dropOldest.
I'll call an MBean defined this way an @MBean.
One way to look at this is that with the existing Standard MBeans,
public methods from the class are picked out as being management
methods by virtue of being in the Standard MBean interface that
the class implements. So in this example the
CacheMBean interface defines which methods in
Cache are the management methods. In the new form,
the methods are picked out by being annotated, and there is no
need to define an interface.
Pros and cons of @MBeans
The new style appears considerably more convenient than
Standard MBeans. You only have to maintain one source file,
rather than managing a class and an interface.
There is a downside, however, which may show up in bigger
projects. The advantage of the Standard MBean approach is that
the MBean interface tells you exactly what the attributes and
operations of the MBean are. There is no extraneous information
in the MBean interface: every method defines an attribute or an
operation.
On the other hand, with @MBeans the management attributes and
operations are potentially mixed in with many other methods,
public or private. So it is not immediately obvious what the
management interface of the MBean is.
This disadvantage applies both when reading the source code and
when looking at the Javadoc output.
A second disadvantage is that it is no longer possible to
construct a
href="http://java.sun.com/javase/6/docs/api/javax/management/JMX.html#newMBeanProxy(javax.management.MBeanServerConnection,%20javax.management.ObjectName,%20java.lang.Class)">proxy.
Proxies simplify client code by allowing it to access attributes
and operations as compiler-checked method calls. They don't
matter if you are only going to interact with your MBeans
through a graphical interface like
href="http://java.sun.com/developer/technicalArticles/J2SE/jconsole.html">JConsole,
but they are a big help if you are writing an application that
will interact with your MBeans specifically.
For smallish projects, these disadvantages are likely to be
minor. Furthermore, it should be possible to define an
href="http://weblogs.java.net/blog/emcmanus/archive/2006/06/using_annotatio.html">annotation
processor that extracts a Standard MBean interface from an
@MBean, so it can be used for documentation and proxying. In
the example above, the annotation processor could create the
CacheMBean interface every time you compile your
program, based on the @ManagedAttribute and
@ManagedOperation annotations in the
Cache class.
Defining an MXBean
The existing
<a
href="http://java.sun.com/javase/6/docs/api/javax/management/MXBean.html">@MXBean</a>annotation can be used instead of
@MBean to definean href="http://weblogs.java.net/blog/emcmanus/archive/2006/02/what_is_an_mxbe.html">MXBean
rather than a Standard MBean.
<b>@MXBean</b>
public class Cache {
...remainder as above...
}
Descriptions
Although the JMX API allows for textual descriptions to be
associated with attributes, operations, and parameters, when you
use a Standard MBean today these descriptions have meaningless
default values. I've written
href="http://weblogs.java.net/blog/emcmanus/archive/2005/07/adding_informat.html">before
about how you can add meaningful descriptions, but it isn't
easy. This is a really obvious use for annotations.
The proposed new @Description annotation can be used with
Standard MBeans, MXBeans, and @MBeans. (Notice that both
columns use the new API here!)
| Tomorrow's Standard MBean | Tomorrow's @MBean |
|---|---|
|
|
We international types will of course be thinking about
internationalization, and I'll have more to say about that
href="#i18n">below.
Finding the MBeanServer and/or ObjectName
Often an MBean needs to know what MBean Server it is registered
in, or what its name is in that MBean Server. To do this it
currently needs to implement the
href="http://java.sun.com/javase/6/docs/api/javax/management/MBeanRegistration.html">MBeanRegistration
callback interface. The required values are passed to that
interface's
href="http://java.sun.com/javase/6/docs/api/javax/management/MBeanRegistration.html#preRegister(javax.management.MBeanServer,%20javax.management.ObjectName)">preRegister
method. But the interface contains three other methods, which
the MBean must implement even if it has nothing interesting to
do in them.
In the new proposal, the
<a
href="http://java.sun.com/javase/6/docs/api/javax/annotation/Resource.html">@Resource</a>annotation from href="http://java.sun.com/javase/6/docs/api/javax/annotation/package-summary.html">
javax.annotationcan be used instead of implementing
MBeanRegistration when all that's needed is todiscover what the MBeanServer or ObjectName is:
| Today | Tomorrow |
|---|---|
|
|
When the MBean is registered, the MBean Server will
href="http://java.sun.com/javaee/5/docs/tutorial/doc/Resources4.html">inject
the appropriate values into these fields.
This possibility is open to all types of MBeans, not just
@MBeans. You could continue to have a Standard MBean as today,
but stop implementing MBeanRegistration in favour
of @Resource annotations.
Simplified notification handling
Today, if an MBean emits notifications then it must implement
the
href="http://java.sun.com/javase/6/docs/api/javax/management/NotificationBroadcaster.html">NotificationBroadcaster
or
href="http://java.sun.com/javase/6/docs/api/javax/management/NotificationEmitter.html">NotificationEmitter
interface. This means it must keep track of the set of listeners,
as listeners are
href="http://java.sun.com/javase/6/docs/api/javax/management/NotificationBroadcaster.html#addNotificationListener(javax.management.NotificationListener,%20javax.management.NotificationFilter,%20java.lang.Object)">added
and
href="http://java.sun.com/javase/6/docs/api/javax/management/NotificationBroadcaster.html#removeNotificationListener(javax.management.NotificationListener)">removed.
It must also define the list of notification types that it can
emit, by implementing
href="http://java.sun.com/javase/6/docs/api/javax/management/NotificationBroadcaster.html#getNotificationInfo()">getNotificationInfo().
In practice, everybody uses the
href="http://java.sun.com/javase/6/docs/api/javax/management/NotificationBroadcasterSupport.html">NotificationBroadcasterSupport
class instead of doing all this work themselves. In the simplest
case, you just inherit from that class, and pass the list of
notification types to the superclass constructor. If you already
have a superclass, then you need to have a private
NotificationBroadcasterSupport instance and delegate
the NotificationBroadcaster methods to it.
New annotations allow you to define the list of notification
types more simply, and to emit notifications without having to
keep track of listeners.
| Today | Tomorrow |
|---|---|
|
|
The @NotificationInfo annotation is what allows
you to avoid constructing an MBeanNotificationInfo
as in the messy "Today" code.
The new SendNotification interface contains just
the method
href="http://java.sun.com/javase/6/docs/api/javax/management/NotificationBroadcasterSupport.html#sendNotification(javax.management.Notification)">sendNotification.
When you register this MBean in the MBean Server, it will inject
an object into the send field which allows the MBean
to send notifications. The MBean no longer has to be concerned
with managing listeners, which happens somewhere behind the
scenes.
Resource injection of SendNotification is
available to all types of MBeans. Defining the notification types
with @NotificationInfo is not available to Dynamic
MBeans, which are expected to provide a complete
href="http://java.sun.com/javase/6/docs/api/javax/management/MBeanInfo.html">MBeanInfo,
including the MBeanNotificationInfo[] array.
More detail than you want to know about
@NotificationInfo appears
href="#notifinfo">below.
You can stop reading now
If your eyes are already glazing over with all this code, you
can safely stop here, and you'll have seen the main ideas. The
remainder of this entry is about secondary items, and further
details about the main ones.
Descriptor contents
In the JMX API included in the Java SE 6 platform, we
introduced a way to define your own annotations to specify
Descriptor contents. So you might define @Units like this:
@Documented @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface <b>Units</b> {
<b>@DescriptorKey("units")
</b>String value();
}
The new API accepts such annotations on classes or methods that
also have the @ManagedX annotations. For example:
@MBean
public class Cache {
...
<b>@Units("bytes")</b>
@ManagedAttribute
public int getUsed() {...}
...
}
We've also added a new @DesriptorFields
annotation, since the use of @DescriptorKey is
somewhat non-obvious, and overkill for occasional use. So you
can achieve the same effect like this:
@MBean
public class Cache {
...
<b>@DescriptorFields("units=bytes")</b>
@ManagedAttribute
public int getUsed() {...}
...
}
This annotation can be used in Standard MBeans and MXBeans as well
as @MBeans and @MXBeans.
Operation impact
The @ManagedOperation annotation has an optional element impact
of type Impact. This is a new enum with values
{INFO, ACTION, ACTION_INFO, UNKNOWN} corresponding
to the integer codes defined by
href="http://java.sun.com/javase/6/docs/api/javax/management/MBeanOperationInfo.html#INFO">MBeanOperationInfo.
So you can do this:
@MBean
public class Cache {
...
<b>@ManagedOperation(impact = Impact.ACTION)</b>
public int dropOldest(int n) {...}
}
You can also apply @ManagedOperation to a method in a
Standard MBean interface or an MXBean interface in order to
specify the impact.
MBean constructors
Each public constructor in an @MBean or @MXBean is converted into an
MBeanConstructorInfo inside the MBean's
MBeanInfo. This is exactly the same as for
existing Standard MBeans and MXBeans.
StandardMBean class
The class
<a
href="http://java.sun.com/javase/6/docs/api/javax/management/StandardMBean.html">StandardMBean</a>can be used to customize an @MBean or an @MXBean in the same way
as for a Standard MBean or MXBean today. You simply supply
null for the mbeanInterface parameter inthe constructor.
Details on Resource injection
The @Resource annotation can be used to inject an
ObjectName, MBeanServer, or
SendNotification. The annotation can be applied to
a field or to a void method with a single
parameter. For example:
@Resource // field injection
private volatile ObjectName name;
private MBeanServer mbs;
@Resource // method injection
private synchronized void setMBeanServer(MBeanServer mbs) {
this.mbs = mbs;
}
The MBean Server determines what to inject based on the
type. The type is either the declared type of the field or
parameter, or it is specified explicitly in the
@Resource annotation. For example, the following
annotations have the same effect:
@Resource
private volatile ObjectName name;
@Resource(<b>type = ObjectName.class</b>)
private volatile <b>Object</b> name;
I don't think the second form will be used very often, but it might
be used to inject the MBeanServer into a field of
type MBeanServerConnection, for example.
The ObjectName (etc) will be injected as many times as there are
appropriate @Resource annotations, including in parent
classes.
@Resource annotations that don't match one of the giventypes are ignored. (Perhaps they are for some other API.) But
even if the type is not recognized,
@Resourcefields and methods must be instance (not static), and
@Resource methods must have exactly one parameterand return void.
I've used volatile in all these examples because the
href="http://www.cs.umd.edu/%7Epugh/java/memoryModel/">Java Memory
Model would not otherwise guarantee that the MBean would actually
see the injected values. For method-based injection,
synchronized is an alternative, provided the MBean
also uses synchronized to access the injected
value. Notice that the same considerations apply to the
existing MBeanRegistration technique.
Resource injection happens after the MBean's
href="http://java.sun.com/javase/6/docs/api/javax/management/MBeanRegistration.html#preRegister%28javax.management.MBeanServer,%20javax.management.ObjectName%29">preRegister
method (if any) is called, but before the MBean is registered in the
MBean Server. If an injection method throws an exception, then
<a
href="http://java.sun.com/javase/6/docs/api/javax/management/MBeanRegistration.html#postRegister%28java.lang.Boolean%29">postRegister(false)</a>will be called and the exception will be thrown in the same way
as for
preRegister.
More on descriptions
In addition to the description text, the @Description
annotation can specify the values of the descriptionResourceBundleBaseName
and descriptionResourceKey fields in the corresponding
<a
href="http://java.sun.com/javase/6/docs/api/javax/management/Descriptor.html">Descriptor</a>.This is enough to allow for internationalization:
@Description(value="some sort of cache",
key="cache.mbean.description",
bundleBaseName="MyResources")
To complete the story here, we need to have something that is
able to apply these Descriptor fields to localize the
MBeanInfo. We have some ideas on what that
something might look like, but they are not yet fully
formed.
More on @NotificationInfo
As I threatened, here is more information than you wanted to
know about @NotificationInfo.
If an MBean has a @NotificationInfo annotation, then that
annotation is translated into an
href="http://java.sun.com/javase/6/docs/api/javax/management/MBeanNotificationInfo.html">MBeanNotificationInfo
in the MBean's
<a
href="http://java.sun.com/javase/6/docs/api/javax/management/MBeanInfo.html">MBeanInfo</a>.MBeanNotificationInfo includes a
href="http://java.sun.com/javase/6/docs/api/javax/management/MBeanFeatureInfo.html#getName()">namewhich is the name of the notification class. It is usually
"javax.management.Notification", but it might be asubclass. So
@NotificationInfo has an optionalnotificationClass element which is aClass<? extends Notification>. Forexample:
@NotificationInfo(types = {AttributeChangeNotification.ATTRIBUTE_CHANGE},
notificationClass = AttributeChangeNotification.class)
If the MBean can emit more than one class of MBean, then it can
use @NotificationInfos:
@NotificationInfos(
@NotificationInfo(types = {"my.first.notif", "my.second.notif"})
@NotificationInfo(types = {AttributeChangeNotification.ATTRIBUTE_CHANGE},
notificationClass = AttributeChangeNotification.class)
)
The @NotificationInfo is applied to an MBean
class, but a @Description on that class applies to
the MBean, not its notifications. The existence of
@NotificationInfos is another reason why we cannot
use @Description straightforwardly.
This is why there is an optional element of type
@Description
inside @NotificationInfo, so you would write:
@NotificationInfo(types={"my.notif.type"},
<b>description=@Description(value="my notification", key="my.notif.descr")</b>)
You cannot use @DescriptorFields, for the same
reason as @Description, so there's another optional
element that allows you to write:
@NotificationInfo(types={"my.notif.type"},
<b>descriptorFields={"foo=bar"}</b>)
Ideas still in progress
We're studying the possibility of providing a way to cause a
notification that is sent every time a given operation is
completed.
We're looking at ways in which an MBean could say what its
href="http://java.sun.com/javase/6/docs/api/javax/management/ObjectName.html">ObjectName
is. Probably the MBean would only provide a subset of the
information needed to construct the name. It's still unclear
exactly what this might look like.
What next?
This is still work in progress, as you'll have gathered. I'm
very much interested in comments and suggestions, either here or
at
href="mailto:jmx-spec-comments@sun.com">jmx-spec-comments@sun.com.
Thanks!
[Tags:
jmx
jsr
jsr255
annotations]
- Login or register to post comments
- Printer-friendly version
- emcmanus's blog
- 9361 reads





