Type-safe MBean Proxies for JEE 1.3

I recently ran across a blog post by Eamonn McManus discussing the use of interfaces to create type-safe proxies for access to MBeans.

To borrow Eamonn’s example he presents an interface that will be implemented by his MBean:

public interface CacheMBean 
{
    public int getSize();
    public void setSize(int x);
    public void dropOldest(int nEntries);
}

Now in JEE 1.3 the documented way to utilize this MBean is (again borrowing code from Eamonn’s post):

MBeanServer mbeanServer = ...get server...
ObjectName objectName = new ObjectName(...);
int size = mbeanServer.getAttribute(objectName, "Size");
mbeanServer.setAttribute(objectName, new Attribute("Size", size * 2));
mbeanServer.invoke(objectName, "dropOldest", new Object[] {25}, new String[] {"int"});

If you’ve coded like this in the past you are probably shaking your head in disgust. If this is your first encounter with MBean access in JEE 1.3 you may be considering a career change. Bear with me a little longer.

As Eamonn points out Tiger (JSE 1.5) and Mustang (JSE 1.6) offer a built in methods for generating a proxy at runtime that allows access to the MBean operations via the interface. This is a good thing almost any way you look at it. You get compile time type checking and smart IDE’s can provide code completion on the MBean methods and arguments.

Unfortunately, like many of you, I’m stuck working with JEE 1.3 running on Mantis. No, that’s not some super secret version of Java on the horizon, it’s our old pal JSE 1.4.2.
Some time ago I needed to expose some MBeans in this environment and I balked at having my clients write code like that presented above. Instead I rolled my own proxy class for MBean access and have been using it ever since. My class was designed on the KISS principle so it basically just translates calls on an interface into MBean operation invocations. This keeps things simple and has been serving me well.

To access Eamonn’s MBean using my proxy would require code like this:

MBeanServer mbeanServer = ...get server...
ObjectName objectName = new ObjectName(...);
CacheMBean cache = (CacheMBean) MBeanProxy.createMBeanProxy(
             mbeanServer, objectName, CacheMBean.class);
cache.setSize(cache.getSize() * 2);
cache.dropOldest(25);   

Much nicer.

Here’s my implementation of MBeanProxy, feel free to use and modify as you see fit.

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.naming.NamingException;

public class MBeanProxy implements InvocationHandler
{
    public static Object createMBeanProxy(final MBeanServer mbeanServer, 
                                final ObjectName name, final Class interfaceClass)
                                throws NamingException, MalformedObjectNameException
    {

        return (createMBeanProxy(mbeanServer, name, new Class[] { interfaceClass }));
    }

    public static Object createMBeanProxy(final MBeanServer mbeanServer, 
                                final ObjectName name, final Class[] interfaceClasses)
                                throws NamingException, MalformedObjectNameException
    {
        final MBeanProxy handler = new MBeanProxy(name, mbeanServer);
        final Object proxy = Proxy.newProxyInstance(handler.getClass().getClassLoader(), 
                                        interfaceClasses, handler);
        return (proxy);
    }

    public MBeanProxy(final ObjectName mbeanName, final MBeanServer mbeanServer)
    {
        this.mbeanName = mbeanName;
        this.mbeanServer = mbeanServer;
    }

    public Object invoke(final Object proxy, final Method method, 
                         final Object[] args) throws Throwable
    {
        final String operation = method.getName();
        final String[] sigs = genSignature(args);
        final Object ret = mbeanServer.invoke(mbeanName, operation, args, sigs);
        return (ret);
    }

    private String[] genSignature(final Object[] args)
    {
        if (null == args)
            return (null);

        final String[] sigs = new String[args.length];
        for (int i = 0; i < args.length; ++i)
        {
            sigs[i] = args[i].getClass().getName();
        }

        return (sigs);
    }

    private MBeanServer mbeanServer;
    private ObjectName mbeanName;
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: