Developing custom MBeans to manage J2EE Applications (Part I)

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

In this first blog entry, we will learn:

  • 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 our MBean 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.

The complete code sample and associated build files are available as a zip file. The code has been tested against WebLogic Server 10.3.1 and JDK6.

Implementing the MBean

We chose to implement an MBean that can be used to manage properties associated with an application. To keep things interesting those properties will be persisted, and originally packaged with the deployed application's ear file.

Among the different types of MBeans, we choose to implement an MXBean. MXBeans have the main advantage to expose model specific types as Open Types. This means that clients interacting with MXBeans do not need to include any jar files containing application custom classes. For our simple example, this doesn't really come into play, but this also doesn't complicate our task either. So MXBean it is.

If you want to know more about MXBean, a tutorial is available here

MBean interface

The MBean interface declares the JMX management interface exposed by the MBean. Java methods translate to JMX attributes ( Java bean getter/setter pattern ) or to JMX operations ( the remaining methods ). More info on the mapping from Java methods to JMX attributes and operations is available in the JMX specification or in this tutorial. The same mapping rules apply to both Standard MBeans and MXBeans.

package blog.wls.jmx.appmbean;import java.util.Map;import java.io.IOException;public interface PropertyConfigMXBean {    public String setProperty(String key, String value) throws IOException;\    public String getProperty(String key);    public Map getProperties();}

The above interface declares one attribute: Properties, and two operations: setProperty and getProperty. Those are the attribute and operations exposed by our MBean to JMX clients.

MBean implemetation

Our MBean implementation relies on the JDK's Properties class to manage and persist the underlying properties. The complete code for the MBean implementation is included below, and specific sections are discussed thereafter.

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.Properties;import javax.management.MBeanServer;import javax.management.ObjectName;import javax.management.MBeanRegistration;public class PropertyConfig implements PropertyConfigMXBean, MBeanRegistration {    private String relativePath_ = null;     private Properties props_ = null;    private File resource_ = null;    public PropertyConfig(String relativePath) throws Exception {        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() {}     }

Our MBean implements the PropertyConfigMXBean interface, as well as the MBeanRegistration interface.

We use the MBeanRegistration.preRegister method to load the persisted properties upon MBean registration. The original properties are included as part of the deployed ear, and accessed using the application's Context ClassLoader:

        ClassLoader cl = Thread.currentThread().getContextClassLoader();        URL resourceUrl = cl.getResource(relativePath_);        resource_ = new File(resourceUrl.toURI());        load();     

The interesting part in the above code is the use of the application's Context ClassLoader to extract the path under which the deployed ear's property file is located. For this to work we must ensure that the preRegister method is triggered by an application Thread, so that we can access the proper ClassLoader. More on this later.

The load and save methods are quite straight forward. The load method use the Properties class to read the properties from the application's property file into the MBean state. The save method use the Properties class to persist the properties from the MBean state back into the application's property file.

Any new property added using the setProperty method is immediately persisted by calling the save method within the setProperty method. On a more complex project, one could expose save as a JMX operation, and build an associated Configuration Session MBean. This is beyond this blog's topic thought.

MBean life-cycle implementation

To register our MBean upon application start, and unregister it upon application stop (or undeploy), we use WebLogic's ApplicationLifecycleListener class. Note: We could have used the J2EE ServletContextListener class in place, but we chose the ApplicationLifecycleListener as it also works for applications that do not include web modules. The complete code is included below:

package blog.wls.jmx.appmbean;import weblogic.application.ApplicationLifecycleListener; import weblogic.application.ApplicationLifecycleEvent; import weblogic.application.ApplicationException; import javax.management.ObjectName; import javax.management.MBeanServer; import javax.naming.InitialContext; public class ApplicationMBeanLifeCycleListener extends ApplicationLifecycleListener {    public void postStart(ApplicationLifecycleEvent evt) throws ApplicationException {         try {             InitialContext ctx = new InitialContext();             MBeanServer mbs  =                 MBeanServer.class.cast( ctx.lookup("java:comp/jmx/runtime") );             PropertyConfig mbean = new PropertyConfig("config/properties.data");            ObjectName oname = new ObjectName(                "blog.wls.jmx.appmbean:type=PropertyConfig,name=myAppProperties");             mbs.registerMBean(mbean, oname);         }         catch (Exception e) {            // Deal with exception            e.printStackTrace();        }     }     public void preStop(ApplicationLifecycleEvent evt) throws ApplicationException {         try {             InitialContext ctx = new InitialContext();             MBeanServer mbs  =                 MBeanServer.class.cast( ctx.lookup("java:comp/jmx/runtime") );             ObjectName oname = new ObjectName(                "blog.wls.jmx.appmbean:type=PropertyConfig,name=myAppProperties");            if ( mbs.isRegistered(oname) ) {                  mbs.unregisterMBean(oname);             }        }         catch (Exception e) {            // Deal with exception            e.printStackTrace();        }     }        }

In the above code we register our MBean in WebLogic's Runtime MBeanServer: ctx.lookup("java:comp/jmx/runtime"). This MBeanServer is present on all WebLogic's processes, and can be used to register application's custom MBeans.

We construct an instance of our MBean: new PropertyConfig("config/properties.data") passing the relative path to the application's property file. The path is relative to the root of the ear file, as we will see in the next section. Remember from the previous section we used the application Context ClassLoader to load the application property file using the relative path provided above: cl.getResource(relativePath_);. This is made possible by the postStart method that is executed by an application Thread.

We register our MBean under the "blog.wls.jmx.appmbean:type=PropertyConfig,name=myAppProperties" ObjectName. We followed the JMX best practices when picking that name. We recommend you do the same as this greatly helps:

Avoid naming collisions. Organize MBeans in MBean browsers. For instance JConsole's MBean tree. Provide a consistent MBean naming structure when MBeans from many different sources are registered in a single MBeanServer.

 

Another point worth mentioning is the importance of unregistering the MBean when the application is stopped. If the MBean is left registered, the application ClassLoader(s) will be pinned and resource will be leaked.

Packaging the ear file

Now we need to package our code and property file so that both can be loaded by the application Context ClassLoader.

The MBean and life cycle code is packaged in a jar as follow:

$ jar tvf sample-mbean-app-mbeans.jar     0 Tue Oct 07 16:35:28 PDT 2009 META-INF/   121 Tue Oct 07 16:35:26 PDT 2009 META-INF/MANIFEST.MF     0 Tue Oct 07 16:35:26 PDT 2009 blog/     0 Tue Oct 07 16:35:26 PDT 2009 blog/wls/     0 Tue Oct 07 16:35:26 PDT 2009 blog/wls/jmx/     0 Tue Oct 07 16:35:26 PDT 2009 blog/wls/jmx/appmbean/  1337 Tue Oct 07 16:35:26 PDT 2009             blog/wls/jmx/appmbean/ApplicationMBeanLifeCycleListener.class  2417 Tue Oct 07 16:35:26 PDT 2009 blog/wls/jmx/appmbean/PropertyConfig.class   408 Tue Oct 07 16:35:26 PDT 2009            blog/wls/jmx/appmbean/PropertyConfigMXBean.class$ 

The jar's manifest includes the following entry: Class-Path: ../... This will ensure that the application's property file can be accessed from the application's ClassLoader. More on this later.

The ear file is packaged as follow:

$ jar tvf sample-mbean-app.ear     0 Tue Oct 07 16:35:28 PDT 2009 META-INF/   102 Tue Oct 07 16:35:26 PDT 2009 META-INF/MANIFEST.MF   602 Tue Oct 0714:50:34 PDT 2009 META-INF/application.xml   719 Tue Oct 07 16:35:26 PDT 2009 app.war     0 Tue Oct 07 16:35:28 PDT 2009 APP-INF/     0 Tue Oct 07 16:35:28 PDT 2009 APP-INF/lib/  3328 Tue Oct 07 16:35:26 PDT 2009 APP-INF/lib/sample-mbean-app-mbeans.jar     0 Tue Oct 07 16:35:28 PDT 2009 config/   105 Tue Oct 07 30 12:46:28 PDT 2009 config/properties.data   422 Tue Oct 07 13:10:52 PDT 2009 META-INF/weblogic-application.xml$

The MBean jar file sample-mbean-app-mbeans.jar is added under the APP-INF/lib directory. Any jars located under that directory will be added to the application's ClassLoader.

The application's property file is added under config/properties.data, and can be accessed by the application's Context ClassLoader using that path. This works because we added the Class-Path: ../.. entry in sample-mbean-app-mbeans.jar's manifest, and we placed that jar under the APP-INF/lib directory.

META-INF/application.xml declares a single war module app.war, that is empty, and whose only purpose is to make our application valid by including at least one J2EE module.

META-INF/weblogic-application.xml declares our ApplicationLifecycleListener:

<?xml version="1.0" encoding="UTF-8"?><weblogic-application xmlns="http://www.bea.com/ns/weblogic/90"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://www.bea.com/ns/weblogic/90   http://www.bea.com/ns/weblogic/90/weblogic-application.xsd">  <listener>    <listener-class>         blog.wls.jmx.appmbean.ApplicationMBeanLifeCycleListener    </listener-class>  </listener></weblogic-application>

Download the code and build file

The above code and associated build files are available as a zip file. Once you have downloaded the zip file, extract its content, and cd to the app_mbean_part1 directory.

In order to build the code and create the application's ear file, you first need to define the WL_HOME environment property to point to your WebLogic binary install. Make sure $WL_HOME/lib/weblogic.jar is valid. Then just execute:

ant

If everything goes well the sample-mbean-app.ear file should now be located under the build directory.

Deploying the ear

The sample-mbean-app.ear file can be deployed as follow:

java -classpath $WL_HOME/lib/weblogic.jar weblogic.Deployer -noexit -name sample-mbean-app -source build/sample-mbean-app.ear -targets jrfServer_admin -adminurl t3://140.87.10.42:7086 -user weblogic -password gumby1234 -deploy

Make sure to substitute:

Your WebLogic server host and port in place of 140.87.10.42:7086. The port value for your WebLogic server is available from the <WLS_INSTANCE_HOME>/config/config.xml file:
 <server>    <name>jrfServer_admin</name>    <listen-port>7086</listen-port>    <listen-address>140.87.10.42</listen-address>  </server>
Make sure you look under the correct server if several servers are defined as part of your config.xml. For instance in the above case we are connecting to the server identified as "jrfServer_admin ".

 

Your WebLogic server name in place of jrfServer_admin Your WebLogic server administrator's login and password in place of weblogic and gumby1234

 

The following command can be used to undeploy the sample-mbean-app application:

java -classpath $WL_HOME/lib/weblogic.jar weblogic.Deployer -noexit -name sample-mbean-app -targets jrfServer_admin -adminurl t3://140.87.10.42:7086 -user weblogic -password gumby1234 -undeploy

Using JConsole to browse and edit our application MBean

Using JConsole to connect to WebLogic's MBeanServers was the subject of an earlier blog entry. I recommand reading through that blog if you want to know more. Here I will just fast forward to the command line used:

jconsole -J-Djava.class.path=$JAVA_HOME/lib/jconsole.jar:$JAVA_HOME/lib/tools.jar:$WL_HOME/lib/wljmxclient.jar -J-Djmx.remote.protocol.provider.pkgs=weblogic.management.remote -debug

Once JConsole is started select the Remote process connection, and fill out the target MBeanServer URI, Username and Password fields:

service:jmx:iiop://140.87.10.42:7086/jndi/weblogic.management.mbeanservers.runtime

Make sure you replace the host and port values with the ones you used in your deployment command line. The username and password values are also the ones you used as part of your deployment command line.

You should now be connected and able to see our application MBean under the blog.wls.jmx.appmbean tree entry:

app_mbean_part1_jconsole1.JPG

You can experience with creating new properties using the setProperty operation, and browsing them with either the getProperty operation or Properties attribute. Notice that the Properties attribute is exposed to JMX clients as a TabularData:

app_mbean_part1_jconsole2.JPG

The conversion between the Map instance returned by our public Map getProperties() method and the TabularData exposed to JMX clients is performed by the MXBean implementation that is part of the JDK. More info on MXBeans and how they convert native types to Open Types is available here.

You might wonder where is the property file persisted. The answer is it depends on the deployment staging mode, and whether the application is deployed as an ear file or an exploded directory. In most cases the file will be located in a temporary directory under the target server. For instance:

servers/jrfServer_admin/tmp/_WL_user/sample-mbean-app/dgiyyk/config/properties.data

In this case, re-deploying the ear (provided the ear was modified) will overwrite the existing property file with the one contained in the ear file. Any new property added since the application was last deployed will be lost. It is also not possible to share the application's properties among several WebLogic servers, as each server gets its own local copy of the property file.

We can work around this by either:

Removing the property file from the ear file, and access it from a well known external location using regular file I/O. Deploying the application as an exploded ear accessible from all targeted WebLogic servers.

java -classpath $WL_HOME/lib/weblogic.jar weblogic.Deployer -noexit -name sample-mbean-app -source build/exploded-ear -targets jrfServer_admin -adminurl t3://140.87.10.42:7086 -user weblogic -password gumby1234 -deploy

Where the exploded-ear directory contains: jar xvf sample-mbean-app.ear.

In this case the property file will be modified under its original location: build/exploded-ear/config/properties.data.

 

Note: If the property file is shared among several MBeans, then some form of synchronization will need to be provided across those MBeans when reading/writing the property file. We won't get into this in this blog.

Aside from JConsole we can also use a Java client to browse and edit our MBean. This is demonstrated in an earlier blog entry. Another possibility is to use Oracle's Enterprise's Manager.

 

What's next?

Even thought our MBean is functional, it cannot be considered production ready. It is lacking descriptions for the MBean itself, its attributes, operations and operation parameters. The operation parameters are also lacking meaningful names as demonstrated below:

app_mbean_part1_jconsole3.JPG

Our goal next time around will be to fix the above issues.

转载于:https://www.cnblogs.com/mengheyun/archive/2010/12/25/1963007.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值