In my last
blog entry, I described how it's possible to get synchronized
access to multiple attributes of the same MBean via the use of a
SnapshotProxy and a
SynchronizedStandardMBean.
This blog entry describes the SnapshotProxy in more detail.
JMX's use of proxies via the
MBeanServerInvocationHandler APIs is extremely powerful, permitting type-safe programming for client applications. Out of the box, JMX provides a simple proxy implementation that forward the typed requests to an MBeanServer using the MBeanServerConnection API.
One can also enrich the code in the proxy logic to do more. Éamonn McManus's blog has an article on writing your own proxy factory to deal with type-safe proxies. In our case, we're interested in atomic access to multiple attributes on the same MBean.
Our SnapshotProxy will use a single call to
MBeanServerConnection.getAttributes() to read the value
of all the attributes of a given MBean in a single shot, and then
respond to 'getter' requests on individual attributes from the data
returned by the single snapshot.
A proxy object is typically generated through a factory class,
responsible for creating all the SnapshotProxy instances.
Here's our ProxyFactory class:
public class ProxyFactory {
public Object getSnapshotProxy(MBeanServerConnection connection,
ObjectName objectName,
Class interfaceClass) {
final InvocationHandler handler =
new SnapshotProxy(connection,
objectName,
interfaceClass);
final Class[] interfaces = new Class[] {interfaceClass };
return Proxy.newProxyInstance(interfaceClass.getClassLoader(),
interfaces,
handler);
}
private static class SnapshotProxy extends MBeanServerInvocationHandler {
...
}
}
|
As you can see in the above, the SnapshotProxy class is actually a private inner class that's only ever referenced by the Factory class.
The SnapshotProxy class's only real job is to implement
the invoke method to implement a method invocation
through reflection.
The proxy's behavior is to try, upon first invocation, to take a single snapshot of all the attributes, and to then respond to accessors to individual attributes by returning the value from the snapshot.
Here's the beginning of our class:
private static class SnapshotProxy extends MBeanServerInvocationHandler {
private MBeanServerConnection connection;
private ObjectName objectName;
private Class interfaceClass;
private Map
|
Note that there's a difference in semantic between JMX's
getAttribute method and the getAttributes
method. Exceptions that would be thrown on a call to
getAttribute are silently swallowed by
getAttributes, the only visible sign of the error being
that the attribute is missing from the returned values. As such, if
our snapshot doesn't contain the requested value, our proxy falls back
to the default proxy behavior to have the exception raised.
There are two key methods missing in the implementation above -
decodeAttributeName() and
fillAttributeMap(). These are detailed below.
decodeAttributeName is responsible for mapping the name
of a method on the MBean interface to the equivalent MBean attribute
name (if such a mapping exists), or returning null if the method is
not an attribute accessor. Remember that 'getters' of attributes in an
MBean interface may have one of two possible signatures:
Type getAttributeName()boolean isAttributeName()Here's the code:
private String decodeAttributeName(String methodName, int argCount, Class returnType) {
String attributeName = null;
if (methodName.startsWith("get")
&& methodName.length() > 3
&& argCount == 0
&& !returnType.equals(Void.TYPE)) {
attributeName = methodName.substring(3);
} else if (methodName.startsWith("is")
&& methodName.length() > 2
&& argCount == 0
&& returnType.equals(Boolean.TYPE)) {
attributeName = methodName.substring(2);
}
return attributeName;
}
|
The method fillAttributeMap() is responsible for filling
a map with the results obtained from a single snapshot call to
getAttributes().
private void fillAttributeMap() {
String[] attributeNames = getAttributeNames(interfaceClass);
try {
Map
|
As you can see, this code calls down to
getAttributeNames, which is responsible for the
introspection of the interface class to find out all the methods that
indicate an accessor to an MBean attribute.
There is a small added complication when introspecting the interface class to obtain the names of all the attributes - one Java interface can extend another Java interface, so it's necessary not only to iterate over the methods in the interface, but also to iterate over those interfaces declared as implemented by this interface.
private String[] getAttributeNames(Class interfaceClass) {
Set
|
And that's it! All the code to implement a SnapshotProxy factory and its simple SnapshotProxy.
You can download the example code detailed above here.
This SnapshotProxy can be useful for more than just
atomic or near-atomic access to MBean attributes (fully atomic only if
the MBean has been registered or implemented using a design pattern
such as the
SynchronizedStandardMBean). The SnapshotProxy also
has interesting network characteristics, since it can reduce the number
of communications with the MBeanServer.
There is still room for improvement, though. Here are a few suggestions:
getAttributeNames(Class
interfaceClass) in a WeakHashMap to avoid using
reflection for each proxy instance.getAttribute, to avoid making the call if the attribute
is requested again.AttributeValueChangeNotifications from the MBean, and
either invalidate itself or permit the snapshot to be refreshed.( Sep 11 2006, 04:00:00 PM CEST ) Permalink Comments [2]
The snapshot proxy would then be able to handle all method calls, like a regular proxy.
Posted by daniel on September 14, 2006 at 06:30 PM CEST #
If you permit set and invoke operations, then it would almost certainly be appropriate to invalidate the snapshot, since these actions may be state-changing for the MBean.
One simple policy is to use the snapshot proxy when constructing information to display (e.g. a web page) and to use a normal proxy when performing control actions.
Posted by Nick on September 15, 2006 at 11:15 AM CEST #