In this article we will create a dynamic JMX MBean, then register & verify it. In earlier article we created simple standard JMX MBean to publish counter. We will improve that to make it dynamic.
Example:
- Create a simple infinite running main program which creates n number of threads (n=5 for our test).
- Each thread has a counter which is periodically incremented.
- Register a dynamic JMX MBean which makes all thread counters available as attributes of MBean to JMX console.
Earlier article example had a predefined class with exact 1 attribute which was exposed as JMX MBean attributes. In this example, we have non-defined number of therads i.e. n threads. So we have to create n attributes at runtime depending on number of threads.
Create JMX Mbean class
To achieve this, we have to implement a special interface provided by java i.e. javax.management.DynamicMBean. In this implementation, we can override certain methods which define structure of our MBean & also provide dynamic values/operations. For ex: we can override getMBeanInfo() which provides list of attributes for this MBean which we can dynamically populate.
Here is the actual code. This has counter per thread which are incremented by different threads. For each thread there will be one attribute added to MBean which will show value of counter for that thread.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
package com.itsallbinary.jmx; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; import javax.management.DynamicMBean; import javax.management.InvalidAttributeValueException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanNotificationInfo; import javax.management.ReflectionException; public class DynamicApplicationInfo implements DynamicMBean { // Counter per thread private static Map<String, Integer> counterPerThread = new HashMap<>(); private MBeanInfo dMBeanInfo = null; public DynamicApplicationInfo() { buildDynamicMBeanInfo(); } // Method to increment counter for given thread. public static Integer incrementOfThread(String threadName) { if (counterPerThread.get(threadName) == null) { counterPerThread.put(threadName, 0); } counterPerThread.put(threadName, counterPerThread.get(threadName) + 1); System.out.println("Incremented for thread " + threadName + " count = " + counterPerThread.get(threadName)); return counterPerThread.get(threadName); } @Override public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { return counterPerThread.get(attribute); } @Override public MBeanInfo getMBeanInfo() { return dMBeanInfo; } private void buildDynamicMBeanInfo() { MBeanAttributeInfo[] dAttributes = new MBeanAttributeInfo[counterPerThread.keySet().size()]; // Dynamically Build one attribute per thread. List<String> threadNames = counterPerThread.keySet().stream().collect(Collectors.toList()); for (int i = 0; i < dAttributes.length; i++) { dAttributes[i] = new MBeanAttributeInfo(threadNames.get(i), Integer.class.getSimpleName(), "Counter for thread", true, false, false); } dMBeanInfo = new MBeanInfo(this.getClass().getName(), null, dAttributes, null, null, new MBeanNotificationInfo[0]); } /* * For this example, we won't have implementation for below methods. */ @Override public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { // No setters for this example } @Override public AttributeList getAttributes(String[] attributes) { // Skip for this example return null; } @Override public AttributeList setAttributes(AttributeList attributes) { // No setters for this example return null; } @Override public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException { // No Operations for this example return null; } } |
Register MBean & test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
package com.itsallbinary.jmx; import java.lang.management.ManagementFactory; import java.util.Date; import javax.management.MBeanServer; import javax.management.ObjectName; public class DynamicJMXMBeanTest { private static int dynamicNumberOfThread = 5; public static void main(String[] args) throws Exception { // Start threads to increment counters. for (int i = 0; i < dynamicNumberOfThread; i++) { Thread thread = new IncrementerThread("Printer-" + i); thread.start(); } // Register dynamic MBean MBeanServer server = ManagementFactory.getPlatformMBeanServer(); ObjectName objectName1 = new ObjectName("com.itsallbinary.jmx:name=dynamicCounter"); server.registerMBean(new DynamicApplicationInfo(), objectName1); // Keep program running. while (true) { System.out.println("Test - " + new Date() + " "); Thread.sleep(1000); } } } class IncrementerThread extends Thread { private String threadName; public IncrementerThread(String threadName) { this.threadName = threadName; DynamicApplicationInfo.incrementOfThread(threadName); } @Override public void run() { while (true) { DynamicApplicationInfo.incrementOfThread(threadName); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } |
Verification
Now run this program through command line or through eclipse. It will register dynamic JMX MBean & keep program running so that we can verify.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Incremented for thread Printer-0 count = 1 Incremented for thread Printer-1 count = 1 Incremented for thread Printer-2 count = 1 Incremented for thread Printer-0 count = 2 Incremented for thread Printer-3 count = 1 Incremented for thread Printer-4 count = 1 Incremented for thread Printer-1 count = 2 Incremented for thread Printer-2 count = 2 Incremented for thread Printer-3 count = 2 Incremented for thread Printer-4 count = 2 Test - Mon May 20 23:37:35 PDT 2019 Incremented for thread Printer-0 count = 3 Incremented for thread Printer-1 count = 3 |
Then go to JDK bin directory in your machine & run jconsole.exe. For ex: “C:\Program Files\Java\jdk-11.0.0\bin\jconsole.exe”. This will open JCONSOLE UI. Here select our application & then it will show MBeans as shown below.
As you can see it created 5 attributes for 5 thread & all of them are showing counts for that thread.