View Javadoc

1   package com.atlassian.plugin.jmx;
2   
3   import java.lang.management.ManagementFactory;
4   import java.util.concurrent.atomic.AtomicInteger;
5   
6   import javax.management.InstanceNotFoundException;
7   import javax.management.MBeanServer;
8   import javax.management.ObjectName;
9   import javax.management.RuntimeMBeanException;
10  
11  import org.hamcrest.Matchers;
12  import org.junit.Before;
13  import org.junit.Rule;
14  import org.junit.Test;
15  import org.junit.rules.ExpectedException;
16  
17  import static org.hamcrest.MatcherAssert.assertThat;
18  import static org.hamcrest.Matchers.containsString;
19  import static org.hamcrest.Matchers.instanceOf;
20  import static org.hamcrest.Matchers.is;
21  
22  public class TestAbstractJmxBridge
23  {
24      private static final boolean NONDEFAULT_BOOLEAN = true;
25      private static final char NONDEFAULT_CHAR = 'A';
26      private static final byte NONDEFAULT_BYTE = 1;
27      private static final short NONDEFAULT_SHORT = 2;
28      private static final int NONDEFAULT_INT = 3;
29      private static final long NONDEFAULT_LONG = 4;
30      private static final float NONDEFAULT_FLOAT = 5;
31      private static final double NONDEFAULT_DOUBLE = 6;
32      private static final String NONDEFAULT_STRING = "B";
33  
34      @Rule
35      public ExpectedException expectedException = ExpectedException.none();
36  
37      @SuppressWarnings ("UnusedDeclaration")
38      public interface SampleMXBean
39      {
40          boolean getBoolean();
41  
42          char getChar();
43  
44          byte getByte();
45  
46          short getShort();
47  
48          int getInt();
49  
50          long getLong();
51  
52          float getFloat();
53  
54          double getDouble();
55  
56          String getString();
57  
58          void operation();
59  
60          String concat(String left, String right);
61      }
62  
63      /**
64       * Test class for abstract {@link AbstractJmxBridge}.
65       * <p/>
66       * As well as a test class, this serves as an example for the idiomatic usage {@link AbstractJmxBridge}.
67       */
68      private static class SampleJmxBridge extends AbstractJmxBridge<SampleMXBean> implements SampleMXBean
69      {
70          /**
71           * Counter for uniqueifying jmx object names.
72           */
73          private static final AtomicInteger nextJmxInstance = new AtomicInteger();
74  
75          private int operationCount;
76  
77          public SampleJmxBridge()
78          {
79              super(JmxUtil.objectName(nextJmxInstance, "Sample"), SampleMXBean.class);
80          }
81  
82          @Override
83          protected SampleMXBean getMXBean()
84          {
85              return this;
86          }
87  
88          public int getOperationCount()
89          {
90              return operationCount;
91          }
92  
93          @Override
94          public boolean getBoolean()
95          {
96              return NONDEFAULT_BOOLEAN;
97          }
98  
99          @Override
100         public char getChar()
101         {
102             return NONDEFAULT_CHAR;
103         }
104 
105         @Override
106         public byte getByte()
107         {
108             return NONDEFAULT_BYTE;
109         }
110 
111         @Override
112         public short getShort()
113         {
114             return NONDEFAULT_SHORT;
115         }
116 
117         @Override
118         public int getInt()
119         {
120             return NONDEFAULT_INT;
121         }
122 
123         @Override
124         public long getLong()
125         {
126             return NONDEFAULT_LONG;
127         }
128 
129         @Override
130         public float getFloat()
131         {
132             return NONDEFAULT_FLOAT;
133         }
134 
135         @Override
136         public double getDouble()
137         {
138             return NONDEFAULT_DOUBLE;
139         }
140 
141         @Override
142         public String getString()
143         {
144             return NONDEFAULT_STRING;
145         }
146 
147         @Override
148         public void operation()
149         {
150             ++operationCount;
151         }
152 
153         @Override
154         public String concat(final String left, final String right)
155         {
156             return left + right;
157         }
158     }
159 
160     private SampleJmxBridge sampleJmxBridge;
161     private MBeanServer platformMBeanServer;
162 
163     @Before
164     public void setUp() throws Exception
165     {
166         sampleJmxBridge = new SampleJmxBridge();
167         platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
168     }
169 
170     @Test
171     public void getObjectNameMentionsType()
172     {
173         final ObjectName objectName = sampleJmxBridge.getObjectName();
174         assertThat(objectName.toString(), containsString("Sample"));
175     }
176 
177     @Test
178     public void notExposedToJmxBeforeRegister() throws Exception
179     {
180         final ObjectName objectName = sampleJmxBridge.getObjectName();
181         expectedException.expect(InstanceNotFoundException.class);
182         expectedException.expectMessage(objectName.toString());
183         platformMBeanServer.invoke(objectName, "operation", new Object[0], new String[0]);
184     }
185 
186     @Test
187     public void registerExposesToJmx() throws Exception
188     {
189         final ObjectName objectName = sampleJmxBridge.getObjectName();
190         sampleJmxBridge.register();
191 
192         final int before = sampleJmxBridge.getOperationCount();
193         platformMBeanServer.invoke(objectName, "operation", new Object[0], new String[0]);
194         assertThat(sampleJmxBridge.getOperationCount(), is(before + 1));
195     }
196 
197     @Test
198     public void primitiveTypesAreReturnedBoxed() throws Exception
199     {
200         // Originally the proxy had some special case code around primitives, so i'm leaving these tests here as they not costly
201 
202         final ObjectName objectName = sampleJmxBridge.getObjectName();
203         sampleJmxBridge.register();
204 
205         final Object aBoolean = platformMBeanServer.getAttribute(objectName, "Boolean");
206         assertThat(aBoolean, instanceOf(Boolean.class));
207         assertThat((Boolean) aBoolean, is(NONDEFAULT_BOOLEAN));
208 
209         final Object aChar = platformMBeanServer.getAttribute(objectName, "Char");
210         assertThat(aChar, instanceOf(Character.class));
211         assertThat((Character) aChar, is(NONDEFAULT_CHAR));
212 
213         final Object aByte = platformMBeanServer.getAttribute(objectName, "Byte");
214         assertThat(aByte, instanceOf(Byte.class));
215         assertThat((Byte) aByte, is(NONDEFAULT_BYTE));
216 
217         final Object aShort = platformMBeanServer.getAttribute(objectName, "Short");
218         assertThat(aShort, instanceOf(Short.class));
219         assertThat((Short) aShort, is(NONDEFAULT_SHORT));
220 
221         final Object anInt = platformMBeanServer.getAttribute(objectName, "Int");
222         assertThat(anInt, instanceOf(Integer.class));
223         assertThat((Integer) anInt, is(NONDEFAULT_INT));
224 
225         final Object aLong = platformMBeanServer.getAttribute(objectName, "Long");
226         assertThat(aLong, instanceOf(Long.class));
227         assertThat((Long) aLong, is(NONDEFAULT_LONG));
228 
229         final Object aFloat = platformMBeanServer.getAttribute(objectName, "Float");
230         assertThat(aFloat, instanceOf(Float.class));
231         assertThat((Float) aFloat, is(NONDEFAULT_FLOAT));
232 
233         final Object aDouble = platformMBeanServer.getAttribute(objectName, "Double");
234         assertThat(aDouble, instanceOf(Double.class));
235         assertThat((Double) aDouble, is(NONDEFAULT_DOUBLE));
236 
237         final Object aString = platformMBeanServer.getAttribute(objectName, "String");
238         assertThat(aString, instanceOf(String.class));
239         assertThat((String) aString, is(NONDEFAULT_STRING));
240     }
241 
242     @Test
243     public void parametersArePassedCorrectly() throws Exception
244     {
245         final ObjectName objectName = sampleJmxBridge.getObjectName();
246         sampleJmxBridge.register();
247 
248         final Object[] params = { "left", "right" };
249         final String[] signature = new String[] { String.class.getName(), String.class.getName() };
250         final Object result = platformMBeanServer.invoke(objectName, "concat", params, signature);
251         assertThat(result, instanceOf(String.class));
252         assertThat((String) result, is("leftright"));
253     }
254 
255     @Test
256     public void notExposedToJmxAfterUnregister() throws Exception
257     {
258         final ObjectName objectName = sampleJmxBridge.getObjectName();
259         sampleJmxBridge.register();
260         sampleJmxBridge.unregister();
261 
262         expectedException.expect(InstanceNotFoundException.class);
263         expectedException.expectMessage(objectName.toString());
264         platformMBeanServer.invoke(objectName, "operation", new Object[0], new String[0]);
265     }
266 
267     @Test
268     public void throwsIllegalStateExceptionIfReferenceIsCleared() throws Exception
269     {
270         // This is as close to testing the garbage collection can be easily managed
271         final ObjectName objectName = sampleJmxBridge.getObjectName();
272         // registerInternal is just register but exposing state we can use to fake garbage collection.
273         final AbstractJmxBridge.WeakMXBeanInvocationHandler<SampleMXBean> invocationHandler = sampleJmxBridge.registerInternal();
274 
275         // Quick check that registerInternal works as expected to validate the test
276         final Object aString = platformMBeanServer.getAttribute(objectName, "String");
277         assertThat(aString, instanceOf(String.class));
278         assertThat((String) aString, is(NONDEFAULT_STRING));
279 
280         invocationHandler.getImplementationReference().clear();
281 
282         expectedException.expect(RuntimeMBeanException.class);
283         expectedException.expectCause(Matchers.<Throwable>instanceOf(IllegalStateException.class));
284         expectedException.expectMessage(objectName.toString());
285         platformMBeanServer.invoke(objectName, "operation", new Object[0], new String[0]);
286     }
287 
288     @Test
289     public void usageAfterReferenceClearsCausesUnregister() throws Exception
290     {
291         final ObjectName objectName = sampleJmxBridge.getObjectName();
292         final AbstractJmxBridge.WeakMXBeanInvocationHandler<SampleMXBean> invocationHandler = sampleJmxBridge.registerInternal();
293         invocationHandler.getImplementationReference().clear();
294 
295         try
296         {
297             platformMBeanServer.invoke(objectName, "operation", new Object[0], new String[0]);
298         }
299         catch(RuntimeMBeanException ermb)
300         {
301             // Expected
302         }
303 
304         // Note that this is the same exception shape as use before registration
305         expectedException.expect(InstanceNotFoundException.class);
306         expectedException.expectMessage(objectName.toString());
307         platformMBeanServer.invoke(objectName, "operation", new Object[0], new String[0]);
308     }
309 }