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