JMX, MXBeans – random thoughts and
ideas
JMX has been around for a while. A very common use for JMX
is to add it as a debug interface to a web application (https://rterp.wordpress.com/tag/mxbean/).
The truth is that JMX is much more than that.
The core of JMX is MBean’s that are created in your
application and can then be manipulated via the JMX framework. The concept of
JMX reminds me of the DCOM of Microsoft architecture and CORBA (see https://jcp.org/en/jsr/detail?id=70
for a bridge between the two). In essence you create an MBean on the server (in
the MBeanServer), and you can then access this information from any other
machine.
The objects in JMX are organized in an ObjectName, which is
a tree hierarchy of names. This concept is actually very similar to the SNMP
MIB structure.
JMX supports attributes, operations and notifications. I
have seen companies build full infrastructures of application monitoring on top
of JMX (including synchronizing information between machines via JMX). Since
you have attributes you can save state within the MBean. With the operations
you can have method actions externalized, and with the notification you
can have external client register for events without the hassle of creating a
message bus or structure for notification (of course spring support of all JMX
functionality: http://docs.spring.io/spring-integration/docs/2.0.0.RC1/reference/html/jmx.html).
Since the concept of the JMX is very open, multiple adapters
have been created around the JMX ecosystem. Java comes with the JConsole application, so
you can easily manage your MBeans even remotely. There are HTML adapters, and
even SNMP adapters. So if you have a center of data that you need to share with
multiple clients in multiple formats you should have a look at JMX:
If you need export you data from JMX to other platforms
(like GraphIte or Gangila) there is a
nice package called JMXTrans that comes with some build in writers and a framework to create new ones (
If all you need is to pass the information over http protocol that you can have
a look at jolokia (http://www.jolokia.org/).
Since is seems for some there is confusion
between the two frameworks, both authors answered in the following question on the
stackoverflow site: http://stackoverflow.com/questions/10151536/what-is-the-difference-between-jolokia-jmxtrans-when-to-choose-one-over-the
MXBean
Any class can be added to the JMX MBeanServer. If all the
external actions (attributes, operations, notifications) use basic java types
then you have no problem. You can use a self-written class, though the reading
client must have this class in its classpath – a big constraint. To solve this
issue JMX added the MXBean (http://docs.oracle.com/javase/7/docs/api/javax/management/MXBean.html)
to support “open” types of java objects.
Oracle: An MXBean is a type of MBean that references only
a predefined set of data types.
In other words by constraining the data types of you bean,
JMX can now translate you “private classes” to a generic format using
CompositeData and TabularData classes.
To use an MXBean you either implement an interface with
MXBean suffix or use the @MXBean annotation. The other constraint of MXBean is
that you must add the annotation @ConstructorProperties to your constructors.
When you use a concrete class in your code the MBeanServer
will serialize the class into a generic class of the types: CompositeData (http://docs.oracle.com/javase/7/docs/api/javax/management/openmbean/CompositeData.html),
TabularData http://docs.oracle.com/javase/7/docs/api/javax/management/openmbean/TabularData.html.
These classes are similar to a hashmap, though they include
information as to the type of each field. This way and client that supports the
JMX MXBean can serialize this object and use it. If your client has your
propriety class then you can easily serialize the MXBean back to your class.
The MBeanServer will validate the field types of all
MXBean’s to make sure you did not use invalid types. Since you can externalize complex
classes, the MXBeanServer will serialize it to one of the classes in the javax.management.openmbean
package.
For most cases you do not need to add any special code,
since one the MXBean is registered in the MBeanServer any access to this server
via a client, the server will property serialize the class. The only case that
you need to write code is the case of notification and using the user data
object.
Notifications
To send a broadcast notification you create the Notification
Object (http://docs.oracle.com/javase/tutorial/jmx/notifs/)
The notification object has some basic field like type, source, message and
other fields. If you want to add your own typed payload you need to use the User
Data object. For complex classes you should be using the CompositeData or the
TabularData.
To create this class you need to specify all the fields and their types,
for example:
String[] names = {"poolName", "usage", "count"
};
Object[] values = { null, null, null };
OpenType[] types = { SimpleType.STRING,
memoryUsageCompositeType,
SimpleType.LONG };
CompositeType compositeType
=
getCompositeType(names, types);
CompositeData data = new CompositeDataSupport(compositeType, names, values);
This is obviously not very type safe or developer oriented. The
class we would like to use would be like:
public class PoolUsage {
String
poolName;
MemoryUsage usage;
Integer
count;
}
To solve this issue we will use a trick with the
MBeanServer. As stated above if your class is an attribute or used in an
operation the MBeanServer will do all the transformations for you. The problem
is in the User Data scenario where you need to manually add the class. The
trick is to have a helper class that inherits from StandardMBean. This class will have an internal
field with your typed class. The trick is then when you want to serialize your
class, you create the help class, assign the field with the value and the get
the same field but via the getAttribute of the StandardMBean. This way the JMX
framework will do the serialization for you. The other direction is the same.
You create the helper class and assign the field using the JMX setAttribute (setting
the composite data you got in the notification) and the retrieve the specific
class using the standard getter.
Sample code:
public class PoolUsageSerializer
extends StandardMBean implements PoolUsageInterface{
private final static String ATTRIBUTE_NAME = "PoolUsage";
protected PoolUsage poolUsage;
public PoolUsage
getPoolUsage() { return poolUsage; }
public void
setPoolUsage(PoolUsage poolUsage) { this.poolUsage = poolUsage; }
public
PoolUsageSerializer() { super(PoolUsageInterface.class, true); }
public static Object
serialize(PoolUsage poolUsage) {
PoolUsageSerializer
me = new
PoolUsageSerializer();
me.setPoolUsage(data);
return me.getAttribute(ATTRIBUTE_NAME);
}
public static PoolUsage
deserialize(Object userData) {
PoolUsageSerializer
me = new
PoolUsageSerializer();
Attribute
attr = new Attribute(ATTRIBUTE_NAME, userData);
me.setAttribute(attr);
return me.getPoolUsage();
}
}
For more information see:
Security
JMX also supports the option to secure the server with user
name and password. You can also assign roles (read/write) per user, and even
secure communications via ssl. For securing your JMX on tomcat see http://tomcat.apache.org/tomcat-7.0-doc/monitoring.html.