IT技术及科技前沿

中文IT博客,为IT专业技术人员提供最全面的信息传播和服务

首页 新随笔 订阅 管理
  • 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.

    posted on 2010-12-25 13:28  孟和2012  阅读(272)  评论(0编辑  收藏  举报