Skip to main content

More on JMX...

Posted by kohsuke on December 17, 2005 at 3:49 PM PST

My previous entry about JMX got some real feedback from people that are working on JMX. In appreciation of that, I wrote a little utility in the hope of contributing back to JMX.

One of the problems I had was the lack of "weak MBean" support ---- I didn't want the MBeanServer to hold a strong reference to my MBean, because that prevents those objects from GC-ed. Eamonn McManus suggested a technique to handle this, but I didn't like that I have to write such class for each MBean that I have.

So instead, I wrote a little utility code that adds weak MBean support to any MBean. It almost makes it look like the MBeanServer supports such functionality by itself. In this way, I didn't need to write a weak MBean wrapper for my MBeans.

The trick behind this is java.lang.reflect.Proxy class, which allows me to generate implementations of any MBean interface at runtime.

<br />package dalma.container;<br /><br />import javax.management.JMException;<br />import javax.management.MBeanRegistration;<br />import javax.management.MBeanServer;<br />import javax.management.ObjectName;<br />import javax.management.StandardMBean;<br />import java.lang.ref.ReferenceQueue;<br />import java.lang.ref.WeakReference;<br />import java.lang.reflect.InvocationHandler;<br />import java.lang.reflect.InvocationTargetException;<br />import java.lang.reflect.Method;<br />import java.lang.reflect.Proxy;<br /><br />/**<br /> * Proxy MBean that avoid strong reference to the real MBean object.<br /> *<br /> * @author Kohsuke Kawaguchi<br /> */<br />final class MBeanProxy implements InvocationHandler, MBeanRegistration {<br /><br />    /**<br />     * Creates a proxy MBean and registers it to the server,<br />     * overriding the existing mbean if necessary.<br />     * <br />     * @param server<br />     *   MBean will be registered to this server.<br />     * @param name<br />     *   The name under which the MBean will be registered<br />     * @param mbeanInterface<br />     *   MBean interface to be exposed<br />     * @param object<br />     *   MBean instance to be exposed<br />     */<br />    public static <T> void register( MBeanServer server, ObjectName name, Class<T> mbeanInterface, T object ) throws JMException {<br />        Object proxy = mbeanInterface.cast(<br />            Proxy.newProxyInstance(mbeanInterface.getClassLoader(),<br />            new Class[]{mbeanInterface,MBeanRegistration.class},<br />            new MBeanProxy(object) ));<br /><br />        if(server.isRegistered(name)) {<br />            try {<br />                server.unregisterMBean(name);<br />            } catch (JMException e) {<br />                // if we fail to unregister, try to register ours anyway.<br />                // maybe a GC kicked in in-between.<br />            }<br />        }<br /><br />        // since the proxy class has random names like '$Proxy1',<br />        // we need to use StandardMBean to designate a management interface<br />        server.registerMBean(new StandardMBean(proxy,mbeanInterface),name);<br /><br />    }<br /><br />    /**<br />     * The real MBean object.<br />     */<br />    private final ReferenceImpl real;<br />    private MBeanServer server;<br />    private ObjectName name;<br /><br />    private MBeanProxy(Object realObject) {<br />        this.real = new ReferenceImpl(realObject);<br />    }<br /><br />    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {<br />        Object o = real.get();<br /><br />        if(method.getDeclaringClass()==MBeanRegistration.class) {<br />            o = this;<br />        }<br /><br />        if(o==null) {<br />            unregister();<br />            throw new IllegalStateException(name+" no longer exists");<br />        }<br /><br />        try {<br />            return method.invoke(o,args);<br />        } catch (InvocationTargetException e) {<br />            if(e.getCause()!=null)<br />                throw e.getCause();<br />            else<br />                throw e;<br />        }<br />    }<br /><br />    private void unregister() {<br />        try {<br />            server.unregisterMBean(name);<br />        } catch (JMException e) {<br />            throw new Error(e); // is this even possible?<br />        }<br />    }<br /><br />    public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {<br />        this.server = server;<br />        this.name = name;<br />        return name;<br />    }<br /><br />    public void postRegister(Boolean registrationDone) {<br />        // noop<br />    }<br /><br />    public void preDeregister() throws Exception {<br />        // noop<br />    }<br /><br />    public void postDeregister() {<br />        server = null;<br />        name = null;<br />    }<br /><br />    private class ReferenceImpl extends WeakReference<Object> {<br />        public ReferenceImpl(Object referent) {<br />            super(referent,queue);<br />        }<br /><br />        public MBeanProxy getProxy() {<br />            return MBeanProxy.this;<br />        }<br />    }<br /><br />    private static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();<br /><br />    static {<br />        Runnable r = new Runnable() {<br />            public void run() {<br />                while (true) {<br />                    ReferenceImpl ref;<br />                    try {<br />                        ref = (ReferenceImpl) (WeakReference<Object>)queue.remove();<br />                    } catch (InterruptedException e) {<br />                        return;<br />                    }<br />                    ref.getProxy().unregister();<br />                }<br />            }<br />        };<br />        Thread t = new Thread(r, "Dalma JMX bean clenaer");<br />        t.setDaemon(true);<br />        t.start();<br />    }<br />}<br />
Related Topics >>