Sunday 3 April 2011

Creating a Dynamic MBean

A dynamic MBean is an MBean that defines its management interface at runtime. For example, a configuration MBean could determine the names and types of the attributes it exposes by parsing an XML file.

A  class needs to implement the interface ‘DynamicMBean’ to qualify as a dynamic MBean. The following class needs a valid Property File location. This property file contains “attribute name = attribute value” pairs, which need to be set in your MBean.  The code has been designed to :

1.        Set the property file’s attributes in  dynamic MBean.

2.       Change dynamic MBean’s attributes and set these values in the property file as well.

3.       Add a new attribute to the MBean, which is not in the property file. This attribute will also be added to your property file.  


public class MyDynamicMBean implements DynamicMBean {

      private final String propertyFileName;

      private final Properties properties;


      public MyDynamicMBean(String propertyFileName) throws IOException {

            this.propertyFileName = propertyFileName;

            this.properties = new Properties();

            load();

      }


      public synchronized String getAttribute(String name)

                  throws AttributeNotFoundException {

            String value = this.properties.getProperty(name);

            if (value != null) {

                  return value;

            }

            throw new AttributeNotFoundException("No such property: " + name);

      }


      public synchronized void setAttribute(Attribute attribute)

                  throws InvalidAttributeValueException, MBeanException,

                  AttributeNotFoundException {

            String name = attribute.getName();

            if (this.properties.getProperty(name) == null)

                  throw new AttributeNotFoundException(name);

            Object value = attribute.getValue();

            if (!(value instanceof String)) {

                  throw new InvalidAttributeValueException(

                              "Attribute value not a string: " + value);

            }

            this.properties.setProperty(name, (String) value);

            try {

                  save();

            } catch (IOException e) {

                  throw new MBeanException(e);

            }

      }


      public void setAttribute(String name, String value) {

            Attribute attribute = new Attribute(name, value);

            AttributeList list = new AttributeList();

            list.add(attribute);

            setAttributes(list);

      }


      public synchronized AttributeList getAttributes(String[] names) {

            AttributeList list = new AttributeList();

            for (String name : names) {

                  String value = this.properties.getProperty(name);

                  if (value != null)

                        list.add(new Attribute(name, value));

            }

            return list;

      }


      public synchronized AttributeList setAttributes(AttributeList list) {

            Attribute[] attrs = (Attribute[]) list.toArray(new Attribute[0]);

            AttributeList retlist = new AttributeList();

            for (Attribute attr : attrs) {

                  String name = attr.getName();

                  Object value = attr.getValue();

                  if ((value instanceof String)) {

                        this.properties.setProperty(name, (String) value);

                        retlist.add(new Attribute(name, value));

                  }

            }

            try {

                  save();

            } catch (IOException e) {

                  return new AttributeList();

            }

            return retlist;

      }


      public Object invoke(String name, Object[] args, String[] sig)

                  throws MBeanException, ReflectionException {

            if ((name.equals("reload")) && ((args == null) || (args.length == 0))

                        && ((sig == null) || (sig.length == 0))) {

                  try {

                        load();

                        return null;

                  } catch (IOException e) {

                        throw new MBeanException(e);

                  }

            }

            throw new ReflectionException(new NoSuchMethodException(name));

      }


      public synchronized MBeanInfo getMBeanInfo() {

            SortedSet names = new TreeSet();

            for (Iterator localIterator1 = this.properties.keySet().iterator(); localIterator1

                        .hasNext();) {

                  Object name = localIterator1.next();

                  names.add((String) name);

            }

            MBeanAttributeInfo[] attrs = new MBeanAttributeInfo[names.size()];

            Iterator it = names.iterator();

            for (int i = 0; i < attrs.length; i++) {

                  String name = (String) it.next();

                  attrs[i] = new MBeanAttributeInfo(name, "java.lang.String",

                              "Property " + name, true, true, false);

            }

            MBeanOperationInfo[] opers = { new MBeanOperationInfo("reload",

                        "Reload properties from file", null, "void", 1) };

            return new MBeanInfo(getClass().getName(), "Property Manager MBean",

                        attrs, null, opers, null);

      }


      private void load() throws IOException {

            InputStream inputStream = new FileInputStream(this.propertyFileName);

            this.properties.load(inputStream);

            inputStream.close();

      }


      private void save() throws IOException {

            String newPropertyFileName = this.propertyFileName;

            File file = new File(newPropertyFileName);

            OutputStream output = new FileOutputStream(file);

            String comment = "Written by " + getClass().getName();

            this.properties.store(output, comment);

            output.close();

            if (!file.renameTo(new File(this.propertyFileName)))

                  throw new IOException("Rename " + newPropertyFileName + " to "

                              + this.propertyFileName + " failed");

      }



}


The best way to use a Dynamic MBean is to  expose properties by using simple calls like below-
JMXFramework.publishJMXProperty(“CassandaraConnection”, 100);

For such an implementation we need a Factory class like:


public class DynamicMBeanFactory {

      private static MyDynamicMBean dynamicMBean;
      public static MBeanServer mbeanServer;
     
      public static MyDynamicMBean getDynamicBean()
      {
            mbeanServer= ManagementFactory.getPlatformMBeanServer();
           
            try {
                  dynamicMBean = new MyDynamicMBean("input/Dynamic.properties");
           
                  mbeanServer.registerMBean(dynamicMBean, new ObjectName(dynamicMBean.getClass().getPackage()
                                    .getName() + ":type=" + dynamicMBean.getClass().getSimpleName()));
                       
                  } catch (Exception e) {
                        e.printStackTrace();
                  }
                  return dynamicMBean;
            }
}


Each MBean object needs a unique identifier in the MBeanSever, of course. That identifier is called the MBean Name, and consists of the JMX Domain and key/value pairs. The whole thing is formatted likeDomainName:key1=val1,key2=val2.

This piece of code sets the platform MBeanServer. On the first call to getPlatformMBeanServer(), it first creates the platform MBeanServer by calling the MBeanServerFactory.createMBeanServer method and registers the platform MXBeans in this platform MBeanServer using the MXBean names defined in the class description. This method, in subsequent calls, will simply return the initially created platform MBeanServer.

An MBean will be created with Object Name  “ com.dynamic.controller:type=MyDynamicMBean” where
“com.dynamic.controller” is the package name of MyDynamicMBean.java.

How to use your Dynamic MBean?


Using your dynamic MBean to set attribute is similar to log.debug(“log message”);


package com.dynamic.controller;

import java.util.Iterator;

import java.util.Set;

import javax.management.MBeanServer;

import javax.management.ObjectInstance;

import javax.management.ObjectName;


public class Main {

      private static final MyDynamicMBean mBean = DynamicMBeanFactory

                  .getDynamicBean();


      public static void main(String[] args) {

            try {


                  mBean.setAttribute("log", "in main class");

                  MBeanServer mbeanServer = DynamicMBeanFactory.mbeanServer;

                  Set dynamicData = mbeanServer.queryMBeans(new ObjectName(

                              " com.dynamic.controller:type=MyDynamicMBean"), null);

                  for (Iterator it = dynamicData.iterator(); it.hasNext();) {

                        ObjectInstance oi = (ObjectInstance) it.next();

                        ObjectName oName = oi.getObjectName();

                        System.out.println(mbeanServer.getAttribute(oName, "log"));

                  }

            } catch (Exception e) {

                  e.printStackTrace();

            }

      }

}



How to see your attributes in JConsole?
1.       Start  JConsole. (To start JConsole open Command Prompt. Go to YOUR_JAVA_HOME/bin and type jconsole.)
2.       If your main method is still in execution , you will find a Local Process with name “com.dynamic.controller.Main ”. Select this process.
3.       Move to MBeans tab. You will find your dynamic MBean there.