View Javadoc

1   package com.atlassian.plugin.jmx;
2   
3   import java.lang.ref.WeakReference;
4   import java.lang.reflect.InvocationHandler;
5   import java.lang.reflect.Method;
6   import java.lang.reflect.Proxy;
7   
8   import javax.management.ObjectName;
9   
10  import com.google.common.annotations.VisibleForTesting;
11  
12  /**
13   * An base class for managing registration and unregistration of a JMX MXBean.
14   * <p/>
15   * This implementation keeps a weak reference to the underlying implementation so that failing to unregister from the platform MBean
16   * server does not leak implementation instances. In the event of usage after the implementation is garbage collected, an {@link
17   * IllegalStateException} is thrown.
18   * <p/>
19   * The idiomatic usage of this class is to subclass, pass the MXBean interface type as parameter, directly implement the MXBean
20   * using a strong reference to the required system components, and override {@link AbstractJmxBridge#getMXBean} to return this. The
21   * {@link AbstractJmxBridge#register()} method will construct a proxy which weakly references this implementation.
22   *
23   * @since v3.0.24
24   */
25  public abstract class AbstractJmxBridge<MXBean>
26  {
27      private final ObjectName objectName;
28      private final Class<MXBean> mxBeanClass;
29  
30      /**
31       * Construct given JMX ObjectName and interface class.
32       *
33       * @param objectName the ObjectName to register under.
34       * @param mxBeanClass the interface class for the MXBean.
35       */
36      public AbstractJmxBridge(final ObjectName objectName, final Class<MXBean> mxBeanClass)
37      {
38          this.objectName = objectName;
39          this.mxBeanClass = mxBeanClass;
40      }
41  
42      /**
43       * Obtain the actual MXBean implementation.
44       * <p/>
45       * The return value is weakly held.
46       *
47       * @return The implementation of the MXBean to expose via JMX.
48       */
49      protected abstract MXBean getMXBean();
50  
51      /**
52       * Register the MXBean with the platform MBean server.
53       */
54      public void register()
55      {
56          registerInternal();
57      }
58  
59  
60      public ObjectName getObjectName()
61      {
62          return objectName;
63      }
64  
65      @VisibleForTesting
66      WeakMXBeanInvocationHandler<MXBean> registerInternal()
67      {
68          final WeakMXBeanInvocationHandler<MXBean> handler = new WeakMXBeanInvocationHandler<MXBean>(objectName, getMXBean());
69          final Object proxy = Proxy.newProxyInstance(mxBeanClass.getClassLoader(), new Class[] { mxBeanClass }, handler);
70          JmxUtil.register(proxy, objectName);
71          return handler;
72      }
73  
74      /**
75       * Unregister the MXBean from the platform MBean server.
76       */
77      public void unregister()
78      {
79          JmxUtil.unregister(objectName);
80      }
81  
82      /**
83       * An InvocationHandler which weakly holds an MXBean and forwards requests to it, defaulting if it is discarded.
84       * <p/>
85       * This inner class is static, and must not retain references to the outer class, either explicit or implicit. Instances of
86       * {@link Proxy} which hold this InvocationHandler are registered with JMX, and the weak reference logic only works with the
87       * simple idiomatic usage of {@link AbstractJmxBridge} if we do not hold outer references.
88       */
89      @VisibleForTesting
90      static class WeakMXBeanInvocationHandler<MXBean> implements InvocationHandler
91      {
92          private final ObjectName objectName;
93          private final WeakReference<MXBean> implementationReference;
94  
95          /**
96           * Construct given implementation.
97           * <p/>
98           * The implementation is weakly held so that registration of this as an MXBean does not stop garbage collection.
99           *
100          * @param objectName the object name of the MXBean which is used in exception messages.
101          * @param implementation the MXBean implementation to forward calls to, weakly held.
102          */
103         public WeakMXBeanInvocationHandler(final ObjectName objectName, final MXBean implementation)
104         {
105             this.objectName = objectName;
106             this.implementationReference = new WeakReference<MXBean>(implementation);
107         }
108 
109         @VisibleForTesting
110         WeakReference<MXBean> getImplementationReference()
111         {
112             return implementationReference;
113         }
114 
115         @Override
116         public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable
117         {
118             final MXBean implementation = implementationReference.get();
119             if (null == implementation)
120             {
121                 // We want to unregister in case our interface classes come from classloaders that need collection.
122                 JmxUtil.unregister(objectName);
123                 throw new IllegalStateException("Cannot use stale MXBean '" + objectName + "'");
124             }
125             else
126             {
127                 return method.invoke(implementation, args);
128             }
129         }
130     }
131 }