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