Tuesday, February 16, 2010

Creating Custom MBeans for WebLogic using Spring

In my previous blog I discussed how WebLogic provides some features to better integrate Spring-enabled apps into the app-server, including WebLogic auto-generated MBeans to monitor Spring specific elements of an application. In this blog, I instead focus on Spring's built-in ability to let developers create their own custom MBeans and how these can then be published to WebLogic's Runtime MBean Server rather than the underlying JVM's default Platform MBean Server.

Why would a developer want to create a custom MBean? Well the developer may want to provide a standards-based management capability for his/her deployed application, to enable third-party tools to manage and monitor the application remotely.

Why would the developer want to publish these MBean's to WebLogic's Runtime MBean Server? Well, in addition to being visible to generic remote JMX client tools (eg. Sun's JConsole tool), the MBeans are then easily accessible from WebLogic specific tools such as the WebLogic Scripting Tool (WLST) and even the WebLogic Diagnostic Framework (WLDF). Also, WebLogic's capabilities can be leveraged to secure the custom MBeans when associated with WebLogic's MBean Servers. For example, one could secure access to the MBeans using the t3s protocol with a valid username and password.

A few months ago, Philippe Le Mouel wrote a great article on how to create custom MBeans and register them with WebLogic, using pretty much standard JavaEE code. As you can see from his article, it takes quite a lot of effort and boilerplate Java code to define an MBean and then register it with the server at start-up.

In contrast, Spring makes it really easy to generate an MBean as part of your developed JavaEE app and avoid a lot of this coding effort. The Spring Reference (chapter 20) describes how to do this in detail. In essence, just include a Java-bean style POJO in your JavaEE web-app, like the following, for example:
package test.management;

public class TestManagerBean {
public String getPropA() {
return propA;
}

public void setPropA(String propA) {
this.propA = propA;
System.out.println("PropA set to: " + propA);
}

public int getPropB() {
return propB;
}

public void setPropB(int propB) {
this.propB = propB;
System.out.println("PropB set to: " + propB);
}

private String propA;
private int propB;
}
This example provides two properties to be JMX-enabled (one String, one int). Obviously, in a real-world application, the getter and setter code would reach inside the rest of the application's code to obtain data or change settings.

In our application's Spring WEB-INF/applicationContext.xml file, we can then define our Spring bean in the normal way with some initial values to be injected into the bean's two properties, e.g.:
<bean id="testMgrBean" class="test.management.TestManagerBean">
<property name="propA" value="Some text"/>
<property name="propB" value="1000"/>
</bean>
The real add-value that Spring then provides, is the ability for Spring to auto-generate the MBean for us, for inclusion in our deployed app, by using Spring's MBeanExporter capability. This is enabled by adding the following definition to our applicationContext.xml file, for example:
<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>
</bean>
Now, when you deploy the app to WebLogic, the MBean is generated and registered with an MBean Server. However, by default, the Spring runtime only really knows about the standard Platform MBean server in the underlying JVM that Spring is running on. As a result, upon deployment, Spring registers this generated MBean with the JVM's built-in Platform MBean server only. Spring has no awareness of the possibility to use one of WebLogic's own MBean Servers. We can demonstrate that the JVM's Platform MBean server is currently hosting the deployed custom MBean, by launching the JDK's 'jconsole' tool, from the same machine as the WebLogic is running on, using the following commands:
$ . /opt/oracle/Middleware/wlserver_10.3/server/bin/setWLSEnv.sh
$ jconsole
We then select the local Java process corresponding to WebLogic to connect to - we don't specify a username/password:

(click image for larger view)

We can then traverse the JVM's Platform MBean Server, using JConsole's built-in MBean browser. As you can see below, in the MBean list, we can view the standard JVM java.lang MBeans, like the OperatingSystem MBean and the Memory MBean, together with our custom MBean which has an ObjectName of com.test:name=TestMgr.

(click image for larger view)

We can even click in the the shown field for one of the properties (e.g. PropA), change the value to "Hello World!", press enter and value is changed in the running MBean. If we view the system-output for the WebLogic Server JVM's O.S. process, we will see the following text logged:
   PropA set to: Hello World!
(we coded this println statement in our example POJO earlier)

What we really want to do though, is have this MBean registered with WebLogic's MBean Server, not the JVM's Platform MBean Server. To do this in Java code we'd have to add a lot of JMX code at application start-up and shut-down time to register/un-register our MBean with the WebLogic MBean Server. However, because we're using Spring, its much easier. In our Spring applicationContext.xml file, we simply add a bean definition to tell Spring to use JNDI to locate the WebLogic Runtime MBean Server object at the well known WebLogic JNDI path. Then we modify our exporter bean definition to set an optional server property of Spring's MBeanExporter, giving it the handle to the JMX Server which Spring should export MBeans to. The additions to the Spring bean definition file are shown below in bold:
<bean id="jmxServerRuntime" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jmx/runtime"/>

</bean>


<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"/>

</bean>
Once re-deployed, we can launch JConsole again, but this time we connect to the WebLogic Runtime MBean Server using T3 to communicate remotely, using the following commands:
$ . /opt/oracle/Middleware/wlserver_10.3/server/bin/setWLSEnv.sh
$ jconsole -J-Djava.class.path=$JAVA_HOME/lib/jconsole.jar:
$JAVA_HOME/lib/tools.jar:$WL_HOME/server/lib/weblogic.jar
-J-Djmx.remote.protocol.provider.pkgs=weblogic.management.remote
For the connection URL we specify "service:jmx:t3://localhost:7001/jndi/weblogic.management.mbeanservers.runtime" and we provide the WebLogic administrator username/password:

(click image for larger view)

We can now see that our MBean is contained in the same JMX list as all WebLogic's server runtime MBeans.

(click image for larger view)

We can even launch WLST to access our custom MBean by looking up the MBean using the JMX APIs and its ObjectName:
$ /opt/oracle/Middleware/wlserver_10.3/common/bin/wlst.sh
> connect('weblogic','welcome1','t3://localhost:7001')
> serverRuntime()
> myMBean = mbs.getObjectInstance(ObjectName('com.test:name=TestMgr'))
> print myMBean
test.management.TestManagerBean[com.test:name=TestMgr]
If we want to get or set a property on the custom MBean using JMX in WLST, we can:
 > print mbs.getAttribute(ObjectName('com.test:name=TestMgr'), 'PropA')
Some text
> mbs.setAttribute(ObjectName('com.test:name=TestMgr'), Attribute('PropA', 'Hello World!'))
> print mbs.getAttribute(ObjectName('com.test:name=TestMgr'), 'PropA')
Hello World!
Or alternatively, we can use the more convenient WLST commands to navigate and manipulate our custom MBean rather than issuing complicated JMX commands:
 > custom()
> ls()
drw- com.test
> cd('com.test')
> ls()
drw- com.test:name=TestMgr
> cd('com.test:name=TestMgr')
> ls()
-rw- PropA Some text
-rw- PropB 1000

> print get('PropA')
Some text
> set('PropA', 'Hello World!')
> print get('PropA')
Hello World!
(remember, when in the custom tree we can't use the WLST cmo variable)

In summary, when it comes to creating management interfaces for developed applications hosted on WebLogic, Spring comes into its own, making things much easier and less error prone. Developers can rely on the comfort of a POJO based programming model for development of application management logic in addition to core business logic.

One final word. At the start of this blog entry I stated that by exporting custom MBeans to WebLogic's MBean Servers, we can use WebLogic tools like WLST and WLDF for monitoring these custom MBeans. Whilst this is true for custom MBeans in general, it turns out that for WLDF and Spring generated MBeans specifically, there's a slight hitch which means you can't use WLDF to harvest a Spring-generated custom MBean's properties or define a Watch/Notify for these properties. In my next blog entry, I intend to explain why this problem occurs and highlight a simple and re-usable workaround to address this and thus enable Spring-generated custom MBeans to be fully usable with WLDF.


Song for today: Just Because by Jane's Addiction

4 comments:

Thomas said...

Hi ,

Could you post a zip file - or rather the .war file or .ear file which is used to deploy to weblogic ?

Sathya Prakash said...

Hello Paul,

Gr8 Post, Can i have a Custom Mbeans with Springs developed as a jar file and placed in the Weblogic Server classpath rather than deploying it as a web application and make it available for access through Jconsole

Paul Done said...

Update to the referenced hyperlink to Philippe Le Mouel's original article on creating custom MBeans on Exalogic: https://blogs.oracle.com/WebLogicServer/entry/developing_custom_mbeans_to_ma

uma said...

hi Paul,
Can you please post this application war/ear file which is used to deployed to Weblogic server ?