Friday, August 15, 2008

Extraordinary Standard MBeans

Standard MBeans are commonly used for several reasons. They are easy to use, the interfaces can be applied to client-side proxies, and the interfaces allow for easy Javadoc-based documentation of the management interface on the Standard MBean. MXBeans share these advantages and add a few of their own such as looser required naming and package conventions and greater interoperability support via Open MBean Types.

However, as discussed in my blog entry The JMX Model MBean, there are some disadvantages of Standard MBeans (and MXBeans) that Model MBeans can address. The most significant reason for using the Model MBean (to wrap existing non-JMX resources such as employed in the Spring Framework) is not possible with Standard or MXBeans due to their static nature. However, the advantage of being able to provide information on operations, parameters, constructors, attributes, and notifications is not a sole advantage of Model MBeans or even of the class of dynamic MBeans. I will demonstrate in this blog entry one approach to specifying operation metadata for Standard MBeans and MXBeans that can be displayed in JConsole and other clients that know how to handle MBeanInfo.

The key to providing additional metadata above and beyond what is normally associated with a Standard MBean is use of the class javax.management.StandardMBean. The description portion of this class's Javadoc documentation shows two ways to use this class. They are using StandardMBean's public constructors or by having the MBean implementation class extend the StandardMBean class (but still implement the same interface). Using the StandardMBean class allows the naming conventions normally associated with Standard MBeans to be relaxed because arbitrary implementation classes and interfaces can be associated with this class. However, in this blog entry, I will keep the normal implementation/interface naming conventions and use the class StandardMBean to provide metadata to the Standard MBean and MXBean.

My first code listing shows a class called DustinMBean that extends StandardMBean so that I can override the StandardMBean.cacheMBeanInfo method.

DustinMBean.java
  1. package dustin.jmx;  
  2.   
  3. import javax.management.MBeanInfo;  
  4. import javax.management.StandardMBean;  
  5.   
  6. /** 
  7.  * Child of StandardMBean overridden for customization purposes. 
  8.  *  
  9.  * @author Dustin 
  10.  */  
  11. public class DustinMBean<t> extends StandardMBean  
  12. {     
  13.    /** 
  14.     * Single constructor accepting MBean implementation class, MBean interface, 
  15.     * and whether or not it is an MXBean (Standard MBean if not MXBean).  Note 
  16.     * that my parent, StandardMBean, has more than one constructor, but I'm only 
  17.     * using one of them. 
  18.     *  
  19.     * @param implementation MBean implementation class. 
  20.     * @param mbeanInterface MBean implementation class's interface. 
  21.     * @param isMXBean true if MXBean; false if Standard MBean. 
  22.     */  
  23.    public DustinMBean(  
  24.       T implementation,  
  25.       Class<t> mbeanInterface,  
  26.       boolean isMXBean )  
  27.    {  
  28.       super(implementation, mbeanInterface, isMXBean);  
  29.    }  
  30.   
  31.    /** 
  32.     * Specify an MBeanInfo to be used with a StandardMBean-turned-DynamicMBean. 
  33.     *  
  34.     * @param mbeanInfo MBeanInfo describing this standard MBean turned into a 
  35.     *    Dynamic MBean. 
  36.     */  
  37.    public void setMBeanInfo(final MBeanInfo mbeanInfo)  
  38.    {  
  39.       cacheMBeanInfo(mbeanInfo);  
  40.    }  
  41. }  
  42. </t></t>  


The class shown above makes it easy to set a Standard MBean's MBeanInfo instance.

I will be using a Standard MBean and an MXBean in this blog entry. The next two code listings show the interface and implementation class for the Standard MBean.

SimpleCalculatorMBean.java (Standard MBean Interface)
  1. package dustin.jmx;  
  2.   
  3. /** 
  4.  * Interface for a Standard MBean example using the Simple Calculator. 
  5.  *  
  6.  * @author Dustin 
  7.  */  
  8. public interface SimpleCalculatorMBean  
  9. {  
  10.    /** 
  11.     * Calculate the sum of the augend and the addend. 
  12.     * 
  13.     * @param augend First integer to be added. 
  14.     * @param addend Second integer to be added. 
  15.     * @return Sum of augend and addend. 
  16.     */  
  17.    public int add(final int augend, final int addend);  
  18.   
  19.    /** 
  20.     * Calculate the difference between the minuend and subtrahend. 
  21.     *  
  22.     * @param minuend Minuend in subtraction operation. 
  23.     * @param subtrahend Subtrahend in subtraction operation. 
  24.     * @return Difference of minuend and subtrahend. 
  25.     */  
  26.    public int subtract(final int minuend, final int subtrahend);  
  27.   
  28.    /** 
  29.     * Calculate the product of the two provided factors. 
  30.     * 
  31.     * @param factor1 First integer factor. 
  32.     * @param factor2 Second integer factor. 
  33.     * @return Product of provided factors. 
  34.     */  
  35.    public int multiply(final int factor1, final int factor2);  
  36.   
  37.    /** 
  38.     * Calculate the quotient of the dividend divided by the divisor. 
  39.     * 
  40.     * @param dividend Integer dividend. 
  41.     * @param divisor Integer divisor. 
  42.     * @return Quotient of dividend divided by divisor. 
  43.     */  
  44.    public double divide(final int dividend, final int divisor);  
  45.   
  46.    /** 
  47.     * Provides the type of operation last executed against this calculator class. 
  48.     *  
  49.     * @return Type of operation last executed against this calculator. 
  50.     */  
  51.    public OperationType whatWasTheLastOperation();  
  52. }  


SimpleCalculator.java (Standard MBean Implementation)
  1. package dustin.jmx;  
  2.   
  3. /** 
  4.  * Simple calculator class intended to demonstrate how a class with no knowledge 
  5.  * of JMX or management can be used as a Standard MBean. 
  6.  *  
  7.  * @author Dustin 
  8.  */  
  9. public class SimpleCalculator implements SimpleCalculatorMBean  
  10. {  
  11.    private OperationType lastOperation = OperationType.NO_OPERATIONS_INVOKED;  
  12.   
  13.    /** 
  14.     * Calculate the sum of the augend and the addend. 
  15.     * 
  16.     * @param augend First integer to be added. 
  17.     * @param addend Second integer to be added. 
  18.     * @return Sum of augend and addend. 
  19.     */  
  20.    public int add(final int augend, final int addend)  
  21.    {  
  22.       lastOperation = OperationType.INTEGER_ADDITION;  
  23.       return augend + addend;  
  24.    }  
  25.   
  26.    /** 
  27.     * Calculate the difference between the minuend and subtrahend. 
  28.     *  
  29.     * @param minuend Minuend in subtraction operation. 
  30.     * @param subtrahend Subtrahend in subtraction operation. 
  31.     * @return Difference of minuend and subtrahend. 
  32.     */  
  33.    public int subtract(final int minuend, final int subtrahend)  
  34.    {  
  35.       lastOperation = OperationType.INTEGER_SUBTRACTION;  
  36.       return minuend - subtrahend;  
  37.    }  
  38.   
  39.    /** 
  40.     * Calculate the product of the two provided factors. 
  41.     * 
  42.     * @param factor1 First integer factor. 
  43.     * @param factor2 Second integer factor. 
  44.     * @return Product of provided factors. 
  45.     */  
  46.    public int multiply(final int factor1, final int factor2)  
  47.    {  
  48.       lastOperation = OperationType.INTEGER_MULTIPLICATION;  
  49.       return factor1 * factor2;  
  50.    }  
  51.   
  52.    /** 
  53.     * Calculate the quotient of the dividend divided by the divisor. 
  54.     * 
  55.     * @param dividend Integer dividend. 
  56.     * @param divisor Integer divisor. 
  57.     * @return Quotient of dividend divided by divisor. 
  58.     */  
  59.    public double divide(final int dividend, final int divisor)  
  60.    {  
  61.       lastOperation = OperationType.INTEGER_DIVISION;  
  62.       return dividend / divisor;  
  63.    }  
  64.   
  65.    /** 
  66.     * Provides the type of operation last executed against this calculator class. 
  67.     *  
  68.     * @return Type of operation last executed against this calculator. 
  69.     */  
  70.    public OperationType whatWasTheLastOperation()  
  71.    {  
  72.       return this.lastOperation;  
  73.    }  
  74. }  


The two code listings immediately above are the interface and implementation for the Standard MBean. The next two listings are for the interface and implementation for a very similar MXBean. The MXBean is not really any more complicated than the Standard MBean, but I needed to name it something different.

LessSimpleCalculatorMXBean.java (MXBean Interface)
  1. package dustin.jmx;  
  2.   
  3. /** 
  4.  * Interface for a standard MXBean example using the Simple Calculator. 
  5.  *  
  6.  * @author Dustin 
  7.  */  
  8. public interface LessSimpleCalculatorMXBean  
  9. {  
  10.    /** 
  11.     * Calculate the sum of the augend and the addend. 
  12.     * 
  13.     * @param augend First integer to be added. 
  14.     * @param addend Second integer to be added. 
  15.     * @return Sum of augend and addend. 
  16.     */  
  17.    public int add(final int augend, final int addend);  
  18.   
  19.    /** 
  20.     * Calculate the difference between the minuend and subtrahend. 
  21.     *  
  22.     * @param minuend Minuend in subtraction operation. 
  23.     * @param subtrahend Subtrahend in subtraction operation. 
  24.     * @return Difference of minuend and subtrahend. 
  25.     */  
  26.    public int subtract(final int minuend, final int subtrahend);  
  27.   
  28.    /** 
  29.     * Calculate the product of the two provided factors. 
  30.     * 
  31.     * @param factor1 First integer factor. 
  32.     * @param factor2 Second integer factor. 
  33.     * @return Product of provided factors. 
  34.     */  
  35.    public int multiply(final int factor1, final int factor2);  
  36.   
  37.    /** 
  38.     * Calculate the quotient of the dividend divided by the divisor. 
  39.     * 
  40.     * @param dividend Integer dividend. 
  41.     * @param divisor Integer divisor. 
  42.     * @return Quotient of dividend divided by divisor. 
  43.     */  
  44.    public double divide(final int dividend, final int divisor);  
  45.   
  46.    /** 
  47.     * Provides the type of operation last executed against this calculator class. 
  48.     *  
  49.     * @return Type of operation last executed against this calculator. 
  50.     */  
  51.    public OperationType whatWasTheLastOperation();  
  52. }  


LessSimpleCalculator.java (MXBean Implementation)
  1. package dustin.jmx;  
  2.   
  3. /** 
  4.  * Simple calculator class intended to demonstrate how a class with no knowledge 
  5.  * of JMX or management can be used as an MXBean. 
  6.  *  
  7.  * @author Dustin 
  8.  */  
  9. public class LessSimpleCalculator implements LessSimpleCalculatorMXBean  
  10. {  
  11.    private OperationType lastOperation = OperationType.NO_OPERATIONS_INVOKED;  
  12.   
  13.    /** 
  14.     * Calculate the sum of the augend and the addend. 
  15.     * 
  16.     * @param augend First integer to be added. 
  17.     * @param addend Second integer to be added. 
  18.     * @return Sum of augend and addend. 
  19.     */  
  20.    public int add(final int augend, final int addend)  
  21.    {  
  22.       lastOperation = OperationType.INTEGER_ADDITION;  
  23.       return augend + addend;  
  24.    }  
  25.   
  26.    /** 
  27.     * Calculate the difference between the minuend and subtrahend. 
  28.     *  
  29.     * @param minuend Minuend in subtraction operation. 
  30.     * @param subtrahend Subtrahend in subtraction operation. 
  31.     * @return Difference of minuend and subtrahend. 
  32.     */  
  33.    public int subtract(final int minuend, final int subtrahend)  
  34.    {  
  35.       lastOperation = OperationType.INTEGER_SUBTRACTION;  
  36.       return minuend - subtrahend;  
  37.    }  
  38.   
  39.    /** 
  40.     * Calculate the product of the two provided factors. 
  41.     * 
  42.     * @param factor1 First integer factor. 
  43.     * @param factor2 Second integer factor. 
  44.     * @return Product of provided factors. 
  45.     */  
  46.    public int multiply(final int factor1, final int factor2)  
  47.    {  
  48.       lastOperation = OperationType.INTEGER_MULTIPLICATION;  
  49.       return factor1 * factor2;  
  50.    }  
  51.   
  52.    /** 
  53.     * Calculate the quotient of the dividend divided by the divisor. 
  54.     * 
  55.     * @param dividend Integer dividend. 
  56.     * @param divisor Integer divisor. 
  57.     * @return Quotient of dividend divided by divisor. 
  58.     */  
  59.    public double divide(final int dividend, final int divisor)  
  60.    {  
  61.       lastOperation = OperationType.INTEGER_DIVISION;  
  62.       return dividend / divisor;  
  63.    }  
  64.   
  65.    /** 
  66.     * Provides the type of operation last executed against this calculator class. 
  67.     *  
  68.     * @return Type of operation last executed against this calculator. 
  69.     */  
  70.    public OperationType whatWasTheLastOperation()  
  71.    {  
  72.       return this.lastOperation;  
  73.    }  
  74. }  


Both the Standard MBean and MXBean code listings above use the OperationType enum. The main reason for adding an enum into the mix is to demonstrate differences between Standard MBean and MXMBean.

OperationType.java
  1. package dustin.jmx;  
  2.   
  3. /** 
  4.  * Simple enum representing a type of calculator operation. 
  5.  *  
  6.  * @author Dustin 
  7.  */  
  8. public enum OperationType  
  9. {  
  10.    INTEGER_ADDITION,  
  11.    INTEGER_SUBTRACTION,  
  12.    INTEGER_MULTIPLICATION,  
  13.    INTEGER_DIVISION,  
  14.    NO_OPERATIONS_INVOKED  
  15. }  


With the Standard MBean, MXBean, enum, and DustinMBean classes all shown above, it is time to look at the class that calls DustinMBean to turn the Standard and MXBeans into dynamic MBeans with metadata descriptions.

StandardMBeanDemonstrator.java
  1. package dustin.jmx;  
  2.   
  3. import java.lang.management.ManagementFactory;  
  4. import javax.management.InstanceAlreadyExistsException;  
  5. import javax.management.MBeanException;  
  6. import javax.management.MBeanInfo;  
  7. import javax.management.MBeanOperationInfo;  
  8. import javax.management.MBeanParameterInfo;  
  9. import javax.management.MBeanRegistrationException;  
  10. import javax.management.MBeanServer;  
  11. import javax.management.MalformedObjectNameException;  
  12. import javax.management.NotCompliantMBeanException;  
  13. import javax.management.ObjectName;  
  14. import javax.management.ReflectionException;  
  15.   
  16. /** 
  17.  * This class is intended to demonstrate a more powerful Standard MBean 
  18.  * available from overriding the javax.management.StandardMBean class. 
  19.  *  
  20.  * @author Dustin 
  21.  */  
  22. public class StandardMBeanDemonstrator  
  23. {  
  24.    /** 
  25.     * Pause for the specified number of milliseconds. 
  26.     *  
  27.     * @param millisecondsToPause Milliseconds to pause execution. 
  28.     */  
  29.    public static void pause(final int millisecondsToPause)  
  30.    {  
  31.       try  
  32.       {  
  33.          Thread.sleep(millisecondsToPause);  
  34.       }  
  35.       catch (InterruptedException threadAwakened)  
  36.       {  
  37.          System.err.println("Don't wake me up!\n" + threadAwakened.getMessage());  
  38.       }  
  39.    }  
  40.   
  41.    /** 
  42.     * Construct the meta information for the SimpleCalculator 
  43.     * Standard-turned-Dynamic MBeans operations and the operations' parameters. 
  44.     *  
  45.     * Note that this method was adapted from a very similar method for 
  46.     * constructor Model MBean operation info data that was discussed in the 
  47.     * method <code>buildModelMBeanOperationInfo()</code> in the class 
  48.     * <code>ModelMBeanDemonstrator</code> in the blog entry "The JMX Model MBean" 
  49.     * (http://marxsoftware.blogspot.com/2008/07/jmx-model-mbean.html). 
  50.     *  
  51.     * @return Metadata about MBean's operations. 
  52.     */  
  53.    private static MBeanOperationInfo[] buildMBeanOperationInfo()  
  54.    {  
  55.       //  
  56.       // Build the PARAMETERS and OPERATIONS meta information for "add".  
  57.       //  
  58.   
  59.       final MBeanParameterInfo augendParameter =  
  60.          new MBeanParameterInfo(  
  61.             "augend",  
  62.             Integer.TYPE.toString(),  
  63.             "The first parameter in the addition (augend)." );  
  64.       final MBeanParameterInfo addendParameter =  
  65.          new MBeanParameterInfo(  
  66.             "addend",  
  67.             Integer.TYPE.toString(),  
  68.             "The second parameter in the addition (addend)." );  
  69.   
  70.       final MBeanOperationInfo addOperationInfo =  
  71.          new MBeanOperationInfo(  
  72.             "add",  
  73.             "Integer Addition",  
  74.             new MBeanParameterInfo[] {augendParameter, addendParameter},  
  75.             Integer.TYPE.toString(),  
  76.             MBeanOperationInfo.INFO );  
  77.   
  78.   
  79.       //  
  80.       // Build the PARAMETERS and OPERATIONS meta information for "subtract".  
  81.       //  
  82.   
  83.       final MBeanParameterInfo minuendParameter =  
  84.          new MBeanParameterInfo(  
  85.             "minuend",  
  86.             Integer.TYPE.toString(),  
  87.             "The first parameter in the substraction (minuend)." );  
  88.   
  89.       final MBeanParameterInfo subtrahendParameter =  
  90.          new MBeanParameterInfo(  
  91.             "subtrahend",  
  92.             Integer.TYPE.toString(),  
  93.             "The second parameter in the subtraction (subtrahend)." );  
  94.   
  95.       final MBeanOperationInfo subtractOperationInfo =  
  96.          new MBeanOperationInfo(  
  97.             "subtract",  
  98.             "Integer Subtraction",  
  99.             new MBeanParameterInfo[] {minuendParameter, subtrahendParameter},  
  100.             Integer.TYPE.toString(),  
  101.             MBeanOperationInfo.INFO );  
  102.   
  103.       //  
  104.       // Build the PARAMETERS and OPERATIONS meta information for "multiply".  
  105.       //  
  106.   
  107.       final MBeanParameterInfo factorOneParameter =  
  108.          new MBeanParameterInfo(  
  109.             "factor1",  
  110.             Integer.TYPE.toString(),  
  111.             "The first factor in the multiplication." );  
  112.   
  113.       final MBeanParameterInfo factorTwoParameter =  
  114.          new MBeanParameterInfo(  
  115.             "factor2",  
  116.             Integer.TYPE.toString(),  
  117.             "The second factor in the multiplication." );  
  118.   
  119.       final MBeanOperationInfo multiplyOperationInfo =  
  120.          new MBeanOperationInfo(  
  121.             "multiply",  
  122.             "Integer Multiplication",  
  123.             new MBeanParameterInfo[] {factorOneParameter, factorTwoParameter},  
  124.             Integer.TYPE.toString(),  
  125.             MBeanOperationInfo.INFO );  
  126.   
  127.       //  
  128.       // Build the PARAMETERS and OPERATIONS meta information for "divide".  
  129.       //  
  130.   
  131.       final MBeanParameterInfo dividendParameter =  
  132.          new MBeanParameterInfo(  
  133.             "dividend",  
  134.             Integer.TYPE.toString(),  
  135.             "The dividend in the division." );  
  136.   
  137.       final MBeanParameterInfo divisorParameter =  
  138.          new MBeanParameterInfo(  
  139.             "divisor",  
  140.             Integer.TYPE.toString(),  
  141.             "The divisor in the division." );  
  142.   
  143.       final MBeanOperationInfo divideOperationInfo =  
  144.          new MBeanOperationInfo(  
  145.             "divide",  
  146.             "Integer Division",  
  147.             new MBeanParameterInfo[] {dividendParameter, divisorParameter},  
  148.             Double.TYPE.toString(),  
  149.             MBeanOperationInfo.INFO );  
  150.   
  151.       //  
  152.       // Build the PARAMETERS and OPERATIONS meta information for  
  153.       // "whatWasTheLastOperation" method.  
  154.       //   
  155.   
  156.       final MBeanOperationInfo lastOperationOperationInfo =  
  157.          new MBeanOperationInfo(  
  158.             "whatWasTheLastOperation",  
  159.             "Last Calculator Operation Performed",  
  160.             null,  
  161.             "OperationType",  
  162.             MBeanOperationInfo.INFO );  
  163.   
  164.       return new MBeanOperationInfo[]  
  165.          { addOperationInfo, subtractOperationInfo,  
  166.            multiplyOperationInfo, divideOperationInfo,  
  167.            lastOperationOperationInfo };  
  168.    
  169.    }  
  170.   
  171.    /** 
  172.     * Create a SimpleCalculator-specific MBeanInfo. 
  173.     *  
  174.     * @param mbeanClassName Name of MBean class being used. 
  175.     * @param description Description for MBean display. 
  176.     * @return Generated MBeanInfo. 
  177.     */  
  178.    private static MBeanInfo createCalculatorMBeanInfo(  
  179.       final String mbeanClassName,  
  180.       final String description )  
  181.    {  
  182.   
  183.       final MBeanInfo mbeanInfo =  
  184.          new MBeanInfo(  
  185.            mbeanClassName,            // underlying class name  
  186.            description,               // description of MBean meant for humans  
  187.            null,                      // attributes  
  188.            null,                      // constructors  
  189.            buildMBeanOperationInfo(), // operations  
  190.            null);                     // notifications  
  191.       return mbeanInfo;  
  192.    }  
  193.   
  194.    /** 
  195.     * Register the provided MBean (implementation and interface provided) with 
  196.     * the provided ObjectName on the provided MBeanServer as a Standard MBean 
  197.     * if isMXBean is false or as an MXBean if isMXBean is true. 
  198.     *  
  199.     * @param mBeanClass MBean implementation class. 
  200.     * @param interfaceClass MBean interface. 
  201.     * @param objectNameStr ObjectName under which MBean will be registered. 
  202.     * @param isMXBean true if this is MXBean; false if Standard MBean. 
  203.     * @param mbs MBean Server on which to register MBean. 
  204.     */  
  205.    private static void registerStandardMBean(  
  206.       final Class mBeanClass,  
  207.       final Class interfaceClass,  
  208.       final String objectNameStr,  
  209.       final boolean isMXBean,  
  210.       final MBeanServer mbs)  
  211.    {  
  212.       try  
  213.       {  
  214.          final Object objectForMBean = mBeanClass.newInstance();  
  215.          final ObjectName objectName = new ObjectName(objectNameStr);  
  216.          final DustinMBean standardMBean =  
  217.             new DustinMBean(  
  218.                mBeanClass.cast(objectForMBean),  
  219.                interfaceClass,  
  220.                isMXBean);  
  221.          standardMBean.setMBeanInfo(  
  222.             createCalculatorMBeanInfo(  
  223.                mBeanClass.getName(),  
  224.                "Simple Standard MBean-turned Dynamic Example.") );  
  225.          mbs.registerMBean(standardMBean, objectName);  
  226.          System.out.println(  
  227.               "MBean with ObjectName " + objectNameStr + " based on class "  
  228.             + mBeanClass.getCanonicalName() + " and interface "  
  229.             + interfaceClass.getCanonicalName() + " has been registered.");  
  230.       }  
  231.       catch (InstantiationException ex)  
  232.       {  
  233.          System.err.println(  
  234.               "Unable to instantiate provided class or interface.\n"  
  235.             + ex.getMessage() );  
  236.       }  
  237.       catch (IllegalAccessException ex)  
  238.       {  
  239.          System.err.println(  
  240.               "Cannot access the provided class for a new instance.\n"  
  241.             + ex.getMessage() );  
  242.       }  
  243.       catch (MalformedObjectNameException ex)  
  244.       {  
  245.          System.err.println(  
  246.               "Cannot create an ObjectName " + objectNameStr + ":\n"  
  247.             + ex.getMessage() );  
  248.       }  
  249.       catch (InstanceAlreadyExistsException ex)  
  250.       {  
  251.          System.err.println(  
  252.               "An MBean instance already exists with name " + objectNameStr  
  253.             + ":\n" + ex.getMessage() );  
  254.       }  
  255.       catch (MBeanRegistrationException ex)  
  256.       {  
  257.          System.err.println(  
  258.               "Failed to register MBean " + objectNameStr + " based on class "  
  259.             + mBeanClass.getCanonicalName() + ":\n" + ex.getMessage() );  
  260.       }  
  261.       catch (NotCompliantMBeanException ex)  
  262.       {  
  263.          System.err.println(  
  264.               "The class " + mBeanClass.getCanonicalName() + " is not a "  
  265.             + "compliant MBean: " + ex.getMessage() );  
  266.       }  
  267.    }  
  268.   
  269.    /** 
  270.     * Create and register an MBean given the provided MBean's implementation 
  271.     * class name, the desired Object Name for the MBean, and the MBeanServer 
  272.     * on which to register the MBean. 
  273.     *  
  274.     * @param mBeanClass MBean implementation class. 
  275.     * @param objectNameStr ObjectName to be used for the MBean instance. 
  276.     * @param mbs MBean Server that will be hosting the MBean. 
  277.     */  
  278.    private static void createAndRegisterStandardMBean(  
  279.       final Class mBeanClass,  
  280.       final String objectNameStr,  
  281.       final MBeanServer mbs)  
  282.    {  
  283.         
  284.       String standardMBeanClassName = "";  
  285.       try  
  286.       {  
  287.          final ObjectName standardMBeanName = new ObjectName(objectNameStr);  
  288.          standardMBeanClassName = mBeanClass.getCanonicalName();  
  289.          mbs.createMBean(standardMBeanClassName, standardMBeanName);  
  290.          System.err.println(  
  291.               "Standard MBean " + standardMBeanName + " created and registered "  
  292.             + "using the class " + standardMBeanClassName );  
  293.       }  
  294.       catch (ReflectionException ex)  
  295.       {  
  296.          System.err.println(  
  297.               "Problem trying to get name of class for MBean:\n"  
  298.             + ex.getMessage() );  
  299.       }  
  300.       catch (InstanceAlreadyExistsException ex)  
  301.       {  
  302.          System.err.println(  
  303.               "MBean already exists and is registered with name "  
  304.             + objectNameStr + ":\n" + ex.getMessage() );  
  305.       }  
  306.       catch (MBeanRegistrationException ex)  
  307.       {  
  308.          System.err.println(  
  309.               "Error trying to register MBean " + objectNameStr + ":\n"  
  310.             + ex.getMessage() );  
  311.       }  
  312.       catch (NotCompliantMBeanException ex)  
  313.       {  
  314.          System.err.println(  
  315.               "Class " + standardMBeanClassName + " is not a compliant MBean:\n"  
  316.             + ex.getMessage() );  
  317.       }  
  318.       catch (MalformedObjectNameException ex)  
  319.       {  
  320.          System.err.println(  "Bad objectname " + objectNameStr + ":\n"  
  321.                             + ex.getMessage() );  
  322.       }  
  323.       catch (MBeanException ex)  
  324.       {  
  325.          System.err.println(  
  326.               "MBeanException encountered trying to register class "  
  327.             + standardMBeanClassName + " as MBean with ObjectName of "  
  328.             + objectNameStr + ":\n" + ex.getMessage() );  
  329.       }  
  330.    }  
  331.   
  332.    /** 
  333.     * Main executable for running Standard and MXBean examples. 
  334.     *  
  335.     * @param arguments The command line arguments. 
  336.     */  
  337.    public static void main(String[] arguments)  
  338.    {  
  339.       final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();  
  340.   
  341.       // Create and register a standard MBean the standard way.  
  342.       createAndRegisterStandardMBean(  
  343.          SimpleCalculator.class,          // SimpleCalculator is Standard MBean  
  344.          "standardmbean:type=standard",   // ObjectName string  
  345.          mbs);                            // MBean server to register on  
  346.   
  347.       // Create and register an MXBean the standard way.  
  348.       createAndRegisterStandardMBean(  
  349.          LessSimpleCalculator.class,  
  350.          "standardmbean:type=mxbean",  
  351.          mbs);  
  352.   
  353.       // Register a standard MBean using the StandardMBean-extended class.  
  354.       registerStandardMBean(  
  355.          SimpleCalculator.class,  
  356.          SimpleCalculatorMBean.class,  
  357.          "dynamicmbean:type=standard",  
  358.          false,  
  359.          mbs);  
  360.   
  361.       // Register an MXBean using the StandardMBean-extended class.  
  362.       registerStandardMBean(  
  363.          LessSimpleCalculator.class,  
  364.          LessSimpleCalculatorMXBean.class,  
  365.          "dynamicmbean:type=mxbean",  
  366.          true,  
  367.          mbs);  
  368.   
  369.       pause(100000);  
  370.    }  
  371. }  


In the class above, the method that sets up the metadata [buildMBeanOperationInfo()] is extremely similar to the same method used in an earlier blog entry to set up metadata on operations for the Model MBean class.

It is inside the try block in the registerStandardMBean method that most of the action occurs in terms of using the StandardMBean-derived DustinMBean. This method is a little more complicated than it would need to be if was less generic and reflection was not used. The example usage code shown in the Javadoc for StandardMBean shows how easy it can be.

For instance, instead of using reflection like this:

  1. final Object objectForMBean = mBeanClass.newInstance();  
  2. final ObjectName objectName = new ObjectName(objectNameStr);  
  3. final DustinMBean standardMBean =  
  4.    new DustinMBean(  
  5.       mBeanClass.cast(objectForMBean),  
  6.       interfaceClass,  
  7.       isMXBean);  
  8. standardMBean.setMBeanInfo(  
  9.    createCalculatorMBeanInfo(  
  10.       mBeanClass.getName(),  
  11.       "Simple Standard MBean-turned Dynamic Example.") );  
  12.       mbs.registerMBean(standardMBean, objectName);  


it could be done less generically but much more simply like this

  1. final ObjectName objectName = new ObjectName(objectNameStr);  
  2. final DustinMBean standardMBean =  
  3.    new DustinMBean(  
  4.       new SimpleCalculator(),  
  5.       SimpleCalculatorMBean.class,  
  6.       isMXBean);  
  7. standardMBean.setMBeanInfo(  
  8.    createCalculatorMBeanInfo(  
  9.       "dustin.jmx.SimpleCalculator",  
  10.       "Simple Standard MBean-turned Dynamic Example.") );  
  11.       mbs.registerMBean(standardMBean, objectName);  


In the main() of the StandardMBeanDemonstrator class, the code demonstrates that the Standard MBean and the MXBean are each used twice, once in the standard way and once via the StandardMBean class. The standard approach depends entirely on naming conventions between implementation and interface while the StandardMBean approach allows the implementation and interface to be explicitly associated in code. The latter approach also allows extra metadata to be specified.

I won't show it here, but the whatWasTheLastOperation() operation only works in JConsole for the two uses of MXBean and not for the two uses of Standard MBean for reasons displayed in a previous blog entry.

I'll end this entry with two screen snapshots. One shows the MXBean that was created and registered with the MBeanServer in the standard way. It works perfectly well, but lacks descriptive information in JConsole. The second shows the same MXBean registered with the StandardMBean approach. It works as well, but also shows dynamic MBean level of description.

MXBean Registered on MBean Server in Standard Way




MXBean Registered on MBean Server via StandardMBean Class




In conclusion, use of the Java SE-provided StandardMBean class allows JMX developers to treat Standard and MXBeans more like dynamic MBeans. Because all MBean types support Descriptors as of Java SE 6, the differences between the types of MBeans continue to blur.

No comments: