The javax.management.MBeanServerInvocationHandler class

Introduction

MX4J version 1.x provided a custom implementation of a JDK 1.3's dynamic proxy to ease invocation of methods on a MBean via the MBeanServer, the mx4j.util.StandardMBeanProxy class.
MX4J 1.x provided also a class for invocation of methods on remote MBeans, namely mx4j.connector.RemoteStandardMBeanProxy based on MX4J's custom remote implementation.
In MX4J 1.x these classes were separated since JMX 1.1 did not specify a super-interface for MBeanServer that could be used also remotely.

In JMX 1.2, the javax.management.MBeanServer interface inherits from the javax.management.MBeanServerConnection interface.
The MBeanServerConnection interface has basically the same methods of MBeanServer except those that does not have sense remotely (like deserialize() and registerMBean()), and adds java.io.IOException in the throws clause of each method, thus making it the "remote" view of a remote MBeanServer.

As of JMX 1.2, both mx4j.util.StandardMBeanProxy and mx4j.connector.RemoteStandardMBeanProxy are obsolete since they have been replaced by one single class, the standard javax.management.MBeanServerInvocationHandler class, that takes advantage of the improved class hierarchy of the javax.management.MBeanServer interface to unify the functionalities that were provided before by the two MX4J classes.

In the following section will be explained how to port old MX4J 1.x code to the new JMX 1.2 code.

MBeanServerInvocationHandler usage

The JMX API to call an MBean via MBeanServer is very tedious: involves a reflection-like syntax and a complex exception handling.
The reflection-like syntax is sometimes an advantage, but it suffers of lack of static type checkings made by the compiler.
The exception handling is complex since it involves unwrapping of javax.management.MBeanExceptions and rethrowing of the original exception thrown by the MBean method, very much like java.lang.reflect.InvocationTargetException requires.

Fortunately, JDK 1.3 provides dynamic proxies via the java.lang.reflect.Proxy class.
By means of dynamic proxies, is it possible to write a proxy that hides the complexity of JMX invocations and provides static type checking and trasparent exception handling.
Compare the two code examples below and note how the second example is cleaner.

Example 2.7. Standard JMX invocation

               
// The ObjectName of the delegate MBean
ObjectName delegateName = ObjectName.getInstance("JMImplementation:type=delegate");

MBeanServer server = ...;

// The MBeanServer ID
String id = null;
try
{
   id = server.getAttribute(delegateName, "MBeanServerId");
}
catch (MBeanException x)
{
   // The getMBeanServerId() method threw an exception ?!?
}
catch(AttributeNotFoundException x)
{
   // Uh ? Not a compliant JMX implementation ?
}
catch (InstanceNotFoundException x)
{
   // Uh ? Not a compliant JMX implementation ?
}
catch (ReflectionException x)
{
  // Uh ? What happened here ?
}
               
            

Example 2.8. JMX invocation with MBeanServerInvocationHandler

               
// The ObjectName of the delegate MBean
ObjectName delegateName = ObjectName.getInstance("JMImplementation:type=delegate");

MBeanServer server = ...;

Object proxy = MBeanServerInvocationHandler.newProxyInstance(server, delegateName, MBeanServerDelegateMBean.class, true);
MBeanServerDelegateMBean delegateMBean = (MBeanServerDelegateMBean)proxy;

// The MBeanServer ID
String id = delegateMBean.getMBeanServerId();
               
            

Usage of the javax.management.MBeanServerInvocationHandler class is straightforward for standard MBeans, since they already comply a management interface that is also a Java interface. This interface can be used directly as argument for the creation of the proxy (the third parameter of the MBeanServerInvocationHandler.newProxyInstance() call).
However, usage of MBeanServerInvocationHandler is not limited to standard MBeans, but also to dynamic MBeans can use it, provided that the management interface they comply to does not change during proxy's life. It is not necessary that the dynamic MBean implements a Java interface: it is enough that the Java interface provided to the MBeanServerInvocationHandler is a (sub)set of the management interface exposed by the dynamic MBean.

The MBeanServerInvocationHandler class can also be used for remote MBeans, in conjuction with the JSR 160 API, like shown in the following code snippet:

Example 2.9. Remote JMX invocation with MBeanServerInvocationHandler

               
// The address of the connector server
JMXServiceURL address = ...;

// Create the JMXCconnectorServer
JMXConnector cntor = JMXConnectorFactory.connect(address);

// Obtain a "stub" for the remote MBeanServer
MBeanServerConnection mbsc = cntor.getMBeanServerConnection();

// The ObjectName of a remote delegate MBean
ObjectName delegateName = ObjectName.getInstance("JMImplementation:type=delegate");

Object proxy = MBeanServerInvocationHandler.newProxyInstance(mbsc, delegateName, MBeanServerDelegateMBean.class, true);
MBeanServerDelegateMBean delegateMBean = (MBeanServerDelegateMBean)proxy;

// The MBeanServer ID
String id = delegateMBean.getMBeanServerId();
               
            

Refer to the javadocs of the javax.management.MBeanServerInvocationHandler class for further details.

Porting examples for mx4j.util.StandardMBeanProxy

The MX4J 1.x API of mx4j.util.StandardMBeanProxy is very similar to javax.management.MBeanServerInvocationHandler's.
Below are shown two code snippets that compare the old MX4J 1.x API with the new standard one.
Note how only the line marked with (*) changes from the old version to the new one.

Example 2.10. Old MX4J 1.x proxy API

               
// The ObjectName of the delegate MBean
ObjectName delegateName = ObjectName.getInstance("JMImplementation:type=delegate");

MBeanServer server = ...;

Object proxy = StandardMBeanProxy.create(MBeanServerDelegateMBean.class, server, delegateName);  (*)
MBeanServerDelegateMBean delegateMBean = (MBeanServerDelegateMBean)proxy;

// The MBeanServer ID
String id = delegateMBean.getMBeanServerId();
               
            

Example 2.11. Standard JMX 1.2 proxy API

               
// The ObjectName of the delegate MBean
ObjectName delegateName = ObjectName.getInstance("JMImplementation:type=delegate");

MBeanServer server = ...;

Object proxy = MBeanServerInvocationHandler.newProxyInstance(server, delegateName, MBeanServerDelegateMBean.class, true);
MBeanServerDelegateMBean delegateMBean = (MBeanServerDelegateMBean)proxy;

// The MBeanServer ID
String id = delegateMBean.getMBeanServerId();