Developing custom MBeans to manage J2EE Applications (Part II)

This is the second part in a series of blogs, that demonstrate how to add management capability to your own application using JMX MBeans.

In Part I we did the bulk of the work. We saw:

  • How to implement a custom MBean to manage configuration associated with an application.
  • How to package the resulting code and configuration as part of the application's ear file.
  • How to register MBeans upon application startup, and unregistered them upon application stop (or undeployment).
  • How to use generic JMX clients such as JConsole to browse and edit our application's MBean.

In this second part, we will add descriptions to our:

  • MBean
  • MBean attributes
  • MBean operations
  • MBean operation parameters

We saw using JConsole, that default descriptions were generated for all of the above. However those default descriptions didn't provide any meaningful information to our MBean users. We will replace those default descriptions with custom descriptions that can be used by us human to understand the functionality provided by our MBean.

We will also add localization support to our MBean. Our goal is to ensure that our MBean's meta-data is localized based on the WebLogic Server it is deployed to. So if that server uses a French Locale, then we'd expect our MBeans descriptions to be French and not English.

The complete code sample and associated build files for part II are available as a zip file. The code has been tested against WebLogic Server 10.3.1 and JDK6. To build and deploy our sample application, please follow the instruction provided in Part I, as they also apply to part II's code and associated zip file.

Providing custom descriptions

In order to provide custom description we need to modify our MBean implementation to extend the StandardMBean class. Despite its name that class is used to implement both Standard and MXBeans.

We override the StandardMBean class many getDescription methods to provide our own custom descriptions. We use resources bundles to store our descriptions to ensure that those can be properly localized. The updated code for our MBean implementation is included below:

package blog.wls.jmx.appmbean;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.File;

import java.net.URL;

import java.util.Map;
import java.util.HashMap;
import java.util.Properties;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.MBeanRegistration;
import javax.management.StandardMBean;
import javax.management.MBeanInfo;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;

public class PropertyConfig extends StandardMBean implements
    PropertyConfigMXBean, MBeanRegistration {

    private String relativePath_ = null; 

    private Properties props_ = null;

    private File resource_ = null;

    private ResourceBundle resourceBundle_ = null;

    private static Map operationsParamNames_ = null;

    static {
        operationsParamNames_ = new HashMap();
        operationsParamNames_.put("setProperty", new String[] {"key", "value"});
        operationsParamNames_.put("getProperty", new String[] {"key"});
    }

    public PropertyConfig(String relativePath) throws Exception {
        super(PropertyConfigMXBean.class , true);
        props_ = new Properties();
        relativePath_ = relativePath;
    }

    public String setProperty(String key,
                              String value) throws IOException {

        String oldValue = null;

        if (value == null) {
            oldValue = String.class.cast(props_.remove(key));
        } else {
            oldValue = String.class.cast(props_.setProperty(key, value));      
        }

        save();
        return oldValue;
    }

    public String getProperty(String key) {
        return props_.getProperty(key);
    }

    public Map getProperties() {
        return (Map) props_;
    }

    private void load() throws IOException {
        
        InputStream is = new FileInputStream(resource_);
        try {
            props_.load(is);
        }
        finally {
            is.close();
        }
    } 

    private void save() throws IOException {
  
        OutputStream os = new FileOutputStream(resource_);

        try {
            props_.store(os, null);
        }
        finally {
            os.close();
        }
    }

    public ObjectName preRegister(MBeanServer server, ObjectName name)
        throws Exception {
       
        // MBean must be registered from an application thread
        // to have access to the application ClassLoader
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        URL resourceUrl = cl.getResource(relativePath_);
        resource_ = new File(resourceUrl.toURI());

        load();     

        return name;
    }

    public void postRegister(Boolean registrationDone) { }

    public void preDeregister() throws Exception {}

    public void postDeregister() {}

    private synchronized ResourceBundle getResourceBundle() {
   
        if ( resourceBundle_ == null ) {
            resourceBundle_ = 
                PropertyResourceBundle.getBundle(
                    "blog.wls.jmx.appmbean.MBeanDescriptions");
        }
       
        return resourceBundle_;
    } 

    protected String getDescription(MBeanAttributeInfo info) { 
        return getResourceBundle().getString("PropertyConfigMXBean.attribute." + 
            info.getName() ); 
    } 

    protected String getDescription(MBeanOperationInfo info) { 
        return getResourceBundle().getString("PropertyConfigMXBean.operation." + 
            info.getName() ); 
    }   

    protected String getDescription(MBeanInfo info) {
        return getResourceBundle().getString("PropertyConfigMXBean.mbean");
    }

    protected String getDescription(MBeanOperationInfo op,
                                    MBeanParameterInfo param,
                                    int sequence) {
        return getResourceBundle().getString(
            "PropertyConfigMXBean.operation." + 
            op.getName() + "." + 
            operationsParamNames_.get(op.getName())[sequence] ); 
    }

    protected String getParameterName(MBeanOperationInfo op,
                                      MBeanParameterInfo param,
                                      int sequence) {
        return operationsParamNames_.get(op.getName())[sequence];
    } 

}

Our default resource bundle class, that contains our English descriptions contains the following code:

package blog.wls.jmx.appmbean;

import java.util.ListResourceBundle;

public class MBeanDescriptions extends ListResourceBundle {
     protected Object[][] getContents() {
         return new Object[][] {
             {"PropertyConfigMXBean.mbean", 
              "MBean used to manage persistent application properties"},  
             {"PropertyConfigMXBean.attribute.Properties", 
              "Properties associated with the running application"},
             {"PropertyConfigMXBean.operation.setProperty", 
              "Create a new property, or change the value of an existing property"},
             {"PropertyConfigMXBean.operation.setProperty.key", 
              "Name that identify the property to set."},
             {"PropertyConfigMXBean.operation.setProperty.value", 
              "Value for the property being set"},
             {"PropertyConfigMXBean.operation.getProperty", 
              "Get the value for an existing property"}, 
             {"PropertyConfigMXBean.operation.getProperty.key", 
              "Name that identify the property to be retrieved"} 
        };
     }
 }

Our MBean is quite simple, and only exposes one attribute and two operations. This helps keep our resource bundle class quite small. For real world example that file will be much bigger. Note: We didn't override the getDescription method associated with our MBean constructor, to keep our sample small as this doesn't add much value to our discussion.

To add support for other languages, we only need to implement the corresponding resource bundle class. MBeanDescriptions_fr for French, and translate the description appropriately.

One interesting thing to note, is the name we used for our resource bundle keys. We didn't use arbitrary values, but we made sure we followed the following convention:

  • MBean description: <MBeanInterfaceClass>.mbean
  • MBean attribute description: <MBeanInterfaceClass>.attribute.<AttributeName>
  • MBean operation description: <MBeanInterfaceClass>.operation.<OperationName>
  • MBean operation parameter description: <MBeanInterfaceClass>.operation.<OperationName>.<ParameterName>
  • MBean constructor description: <MBeanInterfaceClass>.constructor.<ConstructorName>
  • MBean constructor parameter description: <MBeanInterfaceClass>.constructor.<ConstructorName>.<ParameterName>
We also purposely named our resource bundle class MBeanDescriptions and included it as part of the same package as our MBean. The above convention is used by the JDK 7 to localize MBean descriptions without requiring us to extend the StandardMBean class and override its many getDescription methods. Unfortunately JDK 6 doesn't support built-in JMX localization, so we have to write the above code. However we can anticipate the JDK 7 functionality (and possible early support by WebLogic) by using the above convention when specifying our MBean resource bundle and associated resource keys.

You might have noticed the following code in our updated MBean implementatrion:

    protected String getParameterName(MBeanOperationInfo op,
                                      MBeanParameterInfo param,
                                      int sequence) {
        return operationsParamNames_.get(op.getName())[sequence];
    } 
This is used to provide customized name to our operation parameters in place of the default generated 'po', 'p1', ... , 'pn' values.

The result of our hard work can be seen in the following JConsole screen shot:

app_mbean_part2_jconsole1.JPG

Consult Part I for information on how to use JConsole to browse/edit our MBean.

What's next?

What if our application is deployed to a WebLogic server running with an English Locale, and our management client wants to use a French Locale. Currently as things stand the Localization is performed based on the server Locale, and not based on the client's Locale. In the last part of this blog series, we will see how to associate a Locale with our JMX client connection, and we will update our MBean code to support client based localization.

Comments:

Hi, What is the general approach to take if you need to create a custom singleton MBean in a (Weblogic) cluster? Thanks

Posted by Dmitry on November 11, 2009 at 06:17 AM PST #

You can use a startup class to register your MBean in the WLS "Domain Runtime MBeanServer". This is the MBeanServer that runs as part of the "AdminServer" process associated with your domain. More info on startup classes below: http://download.oracle.com/docs/cd/E12840_01/wls/docs103/ConsoleHelp/taskhelp/startup_shutdown/UseStartupAndShutdownClasses.html The startup class will be responsible for registering your MBean when the AdminServer starts up. Since the "Domain Runtime" MBeanServer provides access to the MBeans running on the different managed WebLogic Servers, you can access other server MBeans if necessary from your MBeans as long as those are up and running.

Posted by philippe Le Mouel on November 11, 2009 at 10:23 AM PST #

Thanks a lot Philippe, I've got to try this.

Posted by Dmitry on November 11, 2009 at 09:46 PM PST #

Philippe, Are startup/shutdown classes invoked only once, or once on each server in a cluster?

Posted by Dmitry on November 11, 2009 at 09:56 PM PST #

You can specify the server(s) on which the class should be executed as part of the config.xml configuration.

Posted by philippe Le Mouel on November 18, 2009 at 03:05 AM PST #

Is there a different type of mbean for values that update dynamically (like heap usage)? I'd like to add some more values for monitoring (perhaps network or I/O bytes, for example).

Posted by Bill on November 25, 2009 at 06:15 AM PST #

If the values are always available then any type of MBean will do. You can just fetch the most recent value each time the corresponding getter is called. If the value is not always defined, then you can use a Dynamic MBean whose JMX interface varies based on the availability of the metrics it reports. So for instance if the network connection is off, you might not expose the JMX attributes corresponding to network metrics. I would however caution against Dynamic MBeans whose JMX interface change during the time it is registered, as it can confuse JMX client and human users who expect the JMX interface to be immutable.

Posted by philippe Le Mouel on November 25, 2009 at 07:24 AM PST #

Thanks, that helps. I appreciate the response. It seems I need a value type of long or Long instead of String. If my datatype is long, then tools like JRMC will allow me to graph the values over time. I tried replacing the maps, getters, setters with , etc, but something still isn't quite right. Is this the right approach? I notice that many of the mbeans I see for weblogic are 'CompositeData' I also noticed that the Properties class requires Strings for key and value, so I'm not quite sure how to get around that. Doing a .toSting() on my doubles in my class allowed it to compile, but the run time results are note as expected. -Bill

Posted by Bill on November 25, 2009 at 02:48 PM PST #

The reason WebLogic uses CompositeData types, is to allow generic JMX clients to manage WebLogic without requiring them to add WebLogic's model specific types to their ClassPath. Another advantage is that generic JMX client such as JConsole are built to handle OpenTypes. For instance JConsole can display the content of a TabularData. If you want to add values of type long/Long, you can just use a different data structure than the Properties class used in the sample to store those. You can use a generic collection with instance of type Long, or your own class if you'd rather use long in place of Long. You can then just use Object serialization to persist and restore those form the file system. If the values do not need to be persisted, then the load, save and preRegister methods are not necessary. To expose your long values, you can just add new getters ( and setters if those are mutable values). For instance: public long getMetrci1(); public long getMetrics2(); ...... You will need to add those to both the MBean interface and implementation. You can add a getter per value as illustrated above, or use a single method keyed on the metrics name: public long getMetrics(String metricsName); If the metrics are known in advance, and you have relatively few of them I would use a getter per metrics, as they will be exposed as JMX attributes, and can be graphed using JConsole or other basic JMX tools. If several values are correlated in a single metrics, then I would return them through a single getter: public ComplexMetrics getComplextMetrics(); Where ComplexMetrics is your own type that can be converted to an OpenType ( CompositeData most likely ) by the MXBean engine. More info at: http://java.sun.com/javase/6/docs/api/javax/management/MXBean.html under the "Type Mapping Rules" section. For instance: public class ComplexMetrics{ public long getValue1() {return value1;} public void setValue1(long value1) {this.value1 = value1;} public long getValue2() {return value2;} public void setValue2(long value2) {this.value2 = value2;} public ComplexMetrics() {} private long value1; private long value2; } Hope this helps. Philippe

Posted by philippe Le Mouel on November 26, 2009 at 02:13 AM PST #

I got it now! Thanks for the quick replies and the great info... -Bill

Posted by Bill on November 30, 2009 at 01:56 AM PST #

Hi I want my custom MBean emits notification whenever any client modifies the attribute using setProperty(key, value). For that my Impl class need to extend NotificationBroadcasterSupport and use sendNotification(AttributeChangeNotification). In the above implementation of PropertyConfigMXBean how to achieve this ? Thanks Regards

Posted by Jyotisekhar on June 29, 2010 at 05:11 AM PDT #

You would first modify the MBean implementation to extend StandardEmitterMBean in place of StandardMBean as follow: public class PropertyConfig extends StandardEmitterMBean implements PropertyConfigMXBean, MBeanRegistration { Then as part of the PropertyConfig implementation you will use an instance of NotificationBroadcasterSupport to handle notifications and associated listeners as follow: private static final final MBeanNotificationInfo info = new MBeanNotificationInfo( new String[] {javax.management.AttributeChangeNotification.ATTRIBUTE_CHANGE }, AttributeChangeNotification.class.getName(), "Indicates that an attribute was changed"); public PropertyConfig(String relativePath) throws Exception { super(PropertyConfigMXBean.class , true, new NotificationBroadcasterSupport(info)); props_ = new Properties(); relativePath_ = relativePath; } Finally in the setProperty method you will send the AttributeChangeNotification: public String setProperty(String key, String value) throws IOException { String oldValue = null; if (value == null) { oldValue = String.class.cast(props_.remove(key)); } else { oldValue = String.class.cast(props_.setProperty(key, value)); } save(); AttributeChangeNotification notif = new AttributeChangeNotification(.....); this.sendNotification(notif); return oldValue; }

Posted by philippe Le Mouel on June 29, 2010 at 07:09 AM PDT #

Magnificent post on the blog, I share the same views. I wonder why this valuable society does indeed not contemplate similar to me as well as the blogging site creator :-)

Posted by Traffic Siphon - George Brown &amp; Andrew X on September 10, 2010 at 04:49 AM PDT #

It is truly a delightful piece of writing. An blog post such as this says how sincerely the idea is comprehended by the creator.

Posted by traffic siphon on September 10, 2010 at 05:11 AM PDT #

Very nice blog man... could i use your text in my blog ?ensaios nao destrutivos

Posted by ensaios nao destrutivos on January 12, 2011 at 02:46 PM PST #

This internet site is my inhalation , very excellent design and style and perfect written content .

Posted by conveyancing Solcitors Essex on February 26, 2011 at 08:17 AM PST #

The layout is what really caught my eye, then the i looked at the writing and i think you did a very nice job. Good work:)

Posted by How To Get Backlinks on February 28, 2011 at 05:24 PM PST #

From http://theprosoccershop.com

Posted by Ashly Bebson on March 11, 2011 at 11:23 PM PST #

You made certain nice points there. I did a search on the subject matter and found the majority of folks will go along with with your blog.

Posted by zero friction marketing on March 23, 2011 at 08:17 AM PDT #

hi, very informative piece of info..! thanks a ton.. i have a query.Is it possible for the Mbean operation to accept the argument(or parameter) from a dropdown list? thanks once again

Posted by anu on March 30, 2011 at 03:07 PM PDT #

Its such as you learn my mind! You appear to grasp a lot approximately this, such as you wrote the book in it or something. I feel that you just could do with a few p.c. to drive the message house a little bit, however instead of that, that is excellent blog. A fantastic read. I will certainly be back.

Posted by Shemale cams on April 11, 2011 at 06:43 AM PDT #

Hey! I saw there is something wrong with your site, i wanted to open it, but it didn't loaded completely in my Firefox browser. Can you please see what the problem is and let me know when it's on again? I really have to read this story. Thank you! :)

Posted by Karl Sellai on April 14, 2011 at 08:31 PM PDT #

A property solicitor will act with your best interests at heart throughout the entire transaction regardless of whether you are selling or buying. Property solicitors will deal with almost all aspects of the transaction and if anything is required from you then they would advice you accordingly and this means that your transaction can run smoothly. However, if you do not choose a reliable and experiences property solicitor then you may come unstuck later on in the transaction. For example, some property solicitors mat not communicate enough with their clients and poor communication can lead to confusion and unwanted stress.

Posted by Wai Bech on April 20, 2011 at 08:52 AM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

The official blog for Oracle WebLogic Server fans and followers!

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
5
6
7
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today