Monday, February 22, 2010

WLDF and Spring-generated Custom MBeans

In last week's blog, I mentioned a problem which occurs when trying to configure the WebLogic Diagnostic Framework (WLDF) to reference Spring-generated custom MBeans. Here I will describe how this issue can be addressed in a fairly simple way.

Originally, after deploying a web-app containing a Spring-generated MBean, I had attempted to configure a WLDF module using WebLogic's Admin Console to harvest an attribute of this MBean. However, as shown in the screen-shot below, the WLDF tooling was not detecting this custom MBean type as something that could be monitored.

(click image for larger view)

The Spring-generated custom MBean type is not listed (it should begin with test.management...).

After doing a little digging around, I realised that WebLogic's WLDF documentation at section "Specifying Type Names for WebLogic Server MBeans and Custom MBeans" hints to why this should not work. Specifically it states: "...if the MBean is a ModelMBean and there is no value for the MBean Descriptor field DiagnosticTypeName) then the MBean can't be harvested".

Basically, when you try to define a Collected Metric or Watch Rule in a WLDF Diagnostic Module, WLDF needs to know the MBean's implementation class type for the way WLDF categorises MBeans of interest. Even if we don't use the Admin Console and instead use WLST or hack the XML for the domain config diagnostic module directly, we still have this problem because we have to declare the MBean implementation type.

In Sun's JMX standard, 3 of the 4 possible JMX MBean types (Standard, Dynamic and Open) are implemented directly by a Java class, which WLDF can automatically detect. However, for the 4th type (Model MBean), no direct Java class is used to define the MBean. Instead, the MBean is defined using metadata. As there is no MBean implementation class for WLDF to base its configuration on, it needs a hint for what the implementation class should be assumed to be. Spring-generated custom MBeans are Model MBeans and thus are affected by this issue. The WLDF documentation states that the 'implementation hint' should take the form of an explicitly declared MBean Descriptor field called DiagnosticTypeName.

The left-hand side of the screen-shot below shows the Spring-generated custom MBean, as seen via JConsole. The Model MBean descriptor is missing the WebLogic-specific field and hence the MBean can't be used by WLDF. The right-hand side of the screen-shot shows the generated MBean after the WLDF-required field DiagnosticTypeName has been included (the rest of this blog will show you how to achieve this).

(click image for larger view)

So, for an adequate solution, I really needed a way for Spring to generate MBeans with this field automatically set to an appropriate value. Looking at the documentation for Spring's MBeanExporter (which I described in my last blog entry), I found that MBeanExporter has an optional property called assembler, which, if not defined, defaults to org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler. This default assembler uses reflection to generate Model MBeans by introspecting the simple Java-bean style classes. I didn't really want to lose the power and simplicity of this, but needed some way to ensure that the generated MBeans included the extra descriptor field. Then I hit upon the idea of extending this Spring assembler class and overriding its populateMBeanDescriptor() method to add the extra field after first calling the overridden method to have the other descriptor fields created as normal. So I implemented the following one-off class.
package customjmx;

import javax.management.Descriptor;
import org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler;

public class WLDFAwareReflectiveMBeanInfoAssembler 
                    extends SimpleReflectiveMBeanInfoAssembler {
   private static final String WLDF_MBEAN_TYPE_DESCPTR_KEY = 
                                           "DiagnosticTypeName";
   private static final String NAME_MBEAN_DESCPTR_KEY = "name";
   private static final String MBEAN_KEYNAME_SUFFIX = "MBean";

   @Override
   protected void populateMBeanDescriptor(Descriptor descriptor, 
                           Object managedBean, String beanKey) {
      super.populateMBeanDescriptor(descriptor, managedBean, 
                                                       beanKey);
      descriptor.setField(WLDF_MBEAN_TYPE_DESCPTR_KEY, 
                descriptor.getFieldValue(NAME_MBEAN_DESCPTR_KEY) 
                                        + MBEAN_KEYNAME_SUFFIX);
   }
}
In my example code, I just take the original Spring POJO class's name and add the suffix 'MBean' to come up with a name which I feel best conveys the way the MBean is implemented, for the benefit of WLDF. In my example, the DiagnosticTypeName descriptor field is created for the MBean with a value of test.management.TestManagerBeanMBean. You could easily implement this sub-class code differently to generate a field value using your own convention.

In my Spring bean definition file (WEB-INF/applicationContext.xml) I declared my custom assembler class which extends the Spring default assembler class, and then modified the exporter Spring bean to explicitly reference this assembler, as shown in bold below.
<bean id="assembler" class="customjmx.WLDFAwareReflectiveMBeanInfoAssembler"/>

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"
                                                      lazy-init="false">
   <property name="beans">
      <map>
         <entry key="com.test:name=TestMgr" value-ref="testMgrBean"/>
      </map>
   </property>
   <property name="server" ref="jmxServerRuntime"/>
   <property name="assembler" ref="assembler"/>
</bean>

This time when I re-deployed my web-app to WebLogic and used JConsole to view it, the extra DiagnosticTypeName field was present in the MBean (see the right-hand side of the screen-shot above).

I tried again to create a custom Harvested Metric for an attribute in my Spring generated custom MBean, using WebLogic's Admin Console, and this time I was able to find and select my MBean type as shown in the screen-shot below.

(click image for larger view)

On the next page, I was then able to see the available attributes of the MBean type to monitor and choose the 'PropA' one, as shown below.

(click image for larger view)

Finally, I was given the option to select the instance of the MBean to monitor, before pressing finish to save the WLDF module, as shown in the screen-shot below.

(click image for larger view)

Once my WLDF module was activated, I then waited a few minutes before using JConsole to change, dynamically at runtime, the value of PropA on my custom MBean. I then went to the Admin Console | Diagnostics | Log File page, and selected HarvestedDataArchive as the log file, to view the WLDF harvested data. The screen-shot below shows the value of the harvested 'PropA' attribute, taken every 30 seconds, with the new value shown at the base of the page.

(click image for larger view)

In summary, it is possible to use WLDF to monitor Spring-generated custom MBeans as long as a WebLogic-specific descriptor field is defined for the generated MBean. In this blog, I have shown one way to achieve this, using a simple class that only has to be written once and then re-used and applied for every MBean required in a deployed app.


Song for today: Saints Around My Neck by Come

2 comments:

awatson said...

Thanks so much for posting this. It saved me a lot of time and it worked like a charm. I changed your class to extend MetadataMBeanInfoAssembler instead but other than that, I used your code exactly and it did the trick. Thanks!

Jeffrey A. West said...

Paul - thanks for writing this. This helped me make an example for a customer today!