In this article we will create simple Java Management Extensions i.e. JMX MBean & register them so that it can be viewed through JMX Console.
Example:
- Create a simple infinite running main program which keep incrementing counter periodically.
- Register JMX MBean which makes counter value available to JMX console.
Create MBean class
A standard MBean class MUST follow certain rules as given below.
- There must be a public interface with name <some-name>MBean ex: ApplicationInfoMBean
- This interface must be implemented by concrete class with <some-name> ex: ApplicationInfo
Here is our MBean interface & concrete class class.
1 2 3 4 5 |
package com.itsallbinary.jmx; public interface ApplicationInfoMBean { public long getCounter(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.itsallbinary.jmx; public class ApplicationInfo implements ApplicationInfoMBean { private long counter = 0l; public long incrementCounter() { return counter++; } public long getCounter() { return counter; } } |
Possible errors/mistakes
Failing to follow naming convention will cause NotCompliantMBeanException. For ex: If we name interface as ApplicationInfoBean (‘M’ missing) or if we name concrete class as AppInfo instead of ApplicationInfo then you might see this error.
1 |
Exception in thread "main" javax.management.NotCompliantMBeanException: MBean class com.itsallbinary.jmx.ApplicationInfo does not implement DynamicMBean, and neither follows the Standard MBean conventions (javax.management.NotCompliantMBeanException: Class com.itsallbinary.jmx.ApplicationInfo is not a JMX compliant Standard MBean) nor the MXBean conventions (javax.management.NotCompliantMBeanException: com.itsallbinary.jmx.ApplicationInfo: Class com.itsallbinary.jmx.ApplicationInfo is not a JMX compliant MXBean) |
If you don’t keep interface public (like inner interface or interface in same file etc.) then you might see this error.
1 |
Exception in thread "main" javax.management.NotCompliantMBeanException: Interface is not public: com.itsallbinary.jmx.ApplicationInfoMBean |
Register MBean
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 |
package com.itsallbinary.jmx; import java.lang.management.ManagementFactory; import java.util.Date; import javax.management.MBeanServer; import javax.management.ObjectName; public class JMXBeanTest { public static void main(String[] args) throws Exception { // Create MBean instance ApplicationInfo applicationInfo = new ApplicationInfo(); // Register MBean MBeanServer server = ManagementFactory.getPlatformMBeanServer(); ObjectName objectName = new ObjectName("com.itsallbinary.jmx:name=applicationInfo"); server.registerMBean(applicationInfo, objectName); // Keep program running & increment counter periodically. while (true) { System.out.println("Test - " + new Date() + " " + applicationInfo.incrementCounter()); Thread.sleep(1000); } } } |
Verification
Now run this program through command line or through eclipse. It will register JMX MBean & keep program running so that we can verify.
1 2 3 4 5 6 7 8 9 10 11 |
Test - Mon May 20 22:41:01 PDT 2019 0 Test - Mon May 20 22:41:02 PDT 2019 1 Test - Mon May 20 22:41:03 PDT 2019 2 Test - Mon May 20 22:41:04 PDT 2019 3 Test - Mon May 20 22:41:05 PDT 2019 4 Test - Mon May 20 22:41:06 PDT 2019 5 Test - Mon May 20 22:41:07 PDT 2019 6 Test - Mon May 20 22:41:08 PDT 2019 7 Test - Mon May 20 22:41:09 PDT 2019 8 Test - Mon May 20 22:41:10 PDT 2019 9 Test - Mon May 20 22:41:11 PDT 2019 10 |
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.
Naming syntax examples
<domain>:<key-1>=<value-1>,<key-1>=<value-1>,…
This is the naming syntax for JMX MBeans which helps in properly categorizing MBeans & also make them searchable. For detailed rules & constraints like what is allowed & not allowed etc. refer Oracle documentation.
Lets says, we want to utilize above counter MBean program to count animals & trees of different kinds & want to categorize them properly then this might be a way to do it through MBean naming convention.
We will categorize counters as –
- type=animalCounter
- category=waterAnimals
- subcategory=seaAnimals
- subcategory=lakeAnimals
- category=groundAnimals
- category=flyingAnimals
- category=waterAnimals
- type=treeCounter
- category=fruitTrees
- category=flowerTrees
- subcategory=plants
Here is the code with actual naming convention. Notice names of all MBeans & the key value pairs in the names.
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 |
package com.itsallbinary.jmx; import java.lang.management.ManagementFactory; import java.util.Date; import javax.management.InstanceAlreadyExistsException; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; public class JMXBeanNamingConventionTest { public static void main(String[] args) throws Exception { /* * Animals counter JMX Mbeans */ ApplicationInfo sharkCounter = createMBean( "com.itsallbinary.jmx:type=animalCounter,category=waterAnimals,subcategory=seaAnimals,name=shark"); ApplicationInfo turtleCuunter = createMBean( "com.itsallbinary.jmx:type=animalCounter,category=waterAnimals,name=turtle"); ApplicationInfo dolphinCounter = createMBean( "com.itsallbinary.jmx:type=animalCounter,category=waterAnimals,subcategory=seaAnimals,name=dolphin"); ApplicationInfo catfishCounter = createMBean( "com.itsallbinary.jmx:type=animalCounter,category=waterAnimals,subcategory=lakeAnimals,name=catfish"); ApplicationInfo lionCounter = createMBean( "com.itsallbinary.jmx:type=animalCounter,category=groundAnimals,name=lion"); ApplicationInfo eagleCounter = createMBean( "com.itsallbinary.jmx:type=animalCounter,category=flyingAnimals,name=eagle"); /* * Trees counter JMX Mbeans */ ApplicationInfo orangeTreeCounter = createMBean( "com.itsallbinary.jmx:type=treeCounter,category=fruitTrees,name=orangeTree"); ApplicationInfo rosePlantCounter = createMBean( "com.itsallbinary.jmx:type=treeCounter,category=flowerTrees,subcategory=plants,name=rosePlant"); // Keep program running & increment counter periodically. while (true) { System.out.println("Test - " + new Date() + " " + sharkCounter.incrementCounter() + " " + turtleCuunter.incrementCounter() + " " + dolphinCounter.incrementCounter() + " " + catfishCounter.incrementCounter() + " " + lionCounter.incrementCounter() + " " + eagleCounter.incrementCounter() + " " + orangeTreeCounter.incrementCounter() + " " + rosePlantCounter.incrementCounter()); Thread.sleep(1000); } } private static ApplicationInfo createMBean(String name) throws MalformedObjectNameException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { // Create MBean instance ApplicationInfo applicationInfo = new ApplicationInfo(); // Register MBean MBeanServer server = ManagementFactory.getPlatformMBeanServer(); ObjectName objectName = new ObjectName(name); server.registerMBean(applicationInfo, objectName); return applicationInfo; } } |
When you run this program & verify using JCONSOLE UI, here is how it will look. All the counter JMX MBeans are properly categorized.
Further reading