Extending the WebSphere Application Server administrative system with custom MBeans

You can extend the WebSphere® Application Server administration system by supplying and registering new Java™ Management Extensions (JMX) MBeans (see JMX 1.x Specification for details) in one of the WebSphere processes.

About this task

JMX MBeans represent the management interface for a particular piece of logic. All of the managed resources within the standard product infrastructure are represented as JMX MBeans. There are a variety of ways in which you can create your own MBeans and register them with the JMX MBeanServer running in any WebSphere process. For more information, see MBean Java application programming interface (API) documentation. In this documentation, click Reference > Mbean interfaces.

Procedure

  1. Create custom JMX MBeans.

    You have some alternatives to select from, when creating MBeans to extend the product administrative system. You can use any existing JMX MBean from another application. You can register any MBean that you tested in a JMX MBean server outside of the WebSphere Application Server environment in a product process, including standard MBeans, dynamic MBeans, open MBeans, and model MBeans.

    In addition to any existing JMX MBeans, and ones that were written and tested outside of the product environment, you can use the special distributed extensions provided by WebSphere and create a WebSphere ExtensionMBean provider. This alternative provides better integration with all of the distributed functions of the product administrative system. An ExtensionMBean provider implies that you supply an XML file that contains an MBean Descriptor based on the DTD shipped with the product. This descriptor tells the WebSphere system all of the attributes, operations, and notifications that your MBean supports. With this information, the WebSphere system can route remote requests to your MBean and register remote Listeners to receive your MBean event notifications.

    All of the internal WebSphere MBeans follow the Model MBean pattern. Pure Java classes supply the real logic for management functions, and the WebSphere MBeanFactory class reads the description of these functions from the XML MBean Descriptor and creates an instance of a ModelMBean that matches the descriptor. This ModelMBean instance is bound to your Java classes and registered with the MBeanServer running in the same process as your classes. Your Java code now becomes callable from any WebSphere Application Server administrative client through the ModelMBean created and registered to represent it.

    [z/OS]User MBeans that run on both the WebSphere Application Server distributed platforms and the WebSphere Application Server for z/OS® platform may require special coding to function properly in the z/OS multiprocess model. On distributed platforms where each application server runs in a single Java virtual machine (JVM), there is only one MBean server. The MBean server controls all MBeans that are registered within that application server. On the z/OS platform, there is a control process and a federation of servant processes, each with their own MBean server. The control process has its own MBean proxy server to distribute requests among the servant processes. See the detailed discussion of the JMX MBean multiprocess model request flow.

  2. Optionally define an explicit MBean security policy.

    If you do not define an MBean security policy, the product uses the default security policy.

  3. Register the new MBeans.
    There are various ways to register your MBean.

    You can register your MBean with the WebSphere Application Server administrative service.

    You can register your MBean with the MBeanServer in a WebSphere Application Server process. The following list describes the available options in order of preference:
    • Go through the MBeanFactory class. If you want the greatest possible integration with the WebSphere Application Server system, then use the MBeanFactory class to manage the life cycle of your MBean through the activateMBean and deactivateMBean methods of the MBeanFactory class. Use these methods, by supplying a subclass of the RuntimeCollaborator abstract superclass and an XML MBean descriptor file. Using this approach, you supply a pure Java class that implements the management interface defined in the MBean descriptor. The MBeanFactory class creates the actual ModelMBean and registers it with the product administrative system on your behalf.

      This option is recommended for registering model MBeans.

    • Use the JMXManageable and CustomService interface. You can make the process of integrating with WebSphere administration even easier by implementing a CustomService interface that also implements the JMXManageable interface. Using this approach, you can avoid supplying the RuntimeCollaborator. When your CustomService interface is initialized, the WebSphere MBeanFactory class reads your XML MBean descriptor file and creates, binds, and registers an MBean to your CustomService interface automatically. After the shutdown method of your CustomService is called, the product system automatically deactivates your MBean.
    • Go through the AdminService interface. You can call the registerMBean() method on the AdminService interface and the invocation is delegated to the underlying MBeanServer for the process, after appropriate security checks. You can obtain a reference to the AdminService using the getAdminService() method of the AdminServiceFactory class.

      This option is recommended for registering standard, dynamic, and open MBeans. Implement the UserCollaborator class to use the MBeans and to provide a consistent level of support for them across distributed and z/OS platforms.

      [z/OS]For the z/OS platform, an MBean registered through the registerMBean() method on the AdminService interface is not visible from outside the server and can only be invoked from within the servant process in which it was registered.

    • Get MBeanServer instances directly. You can get a direct reference to the JMX MBeanServer instance running in any product process, by calling the getMBeanServer() method of the MBeanFactory class. You get a reference to the MBeanFactory class by calling the getMBeanFactory() method of the AdminService interface.

      When a custom MBean is registered directly with the MBean server, the MBean object name is enhanced with the cell, node and process name keys by default. This registration allows the MBean to participate in the distributed features of the administrative system. You can turn off the default behavior by setting the com.ibm.websphere.mbeans.disableRouting custom property.

      See Administration services custom properties for more information on how to set and use the com.ibm.websphere.mbeans.disableRouting custom property.

Results

Regardless of the approach used to create and register your MBean, you must set up proper Java 2 security permissions for your new MBean code. The WebSphere AdminService and MBeanServer are tightly protected using Java 2 security permissions and if you do not explicitly grant your code base permissions, security exceptions are thrown when you attempt to invoke methods of these classes. If you are supplying your MBean as part of your application, you can set the permissions in the was.policy file that you supply as part of your application metadata. If you are using a CustomService interface or other code that is not delivered as an application, you can edit the library.policy file in the node configuration, or even the server.policy file in the properties directory for a specific installation.

[z/OS]

Example: The SampleStateMBean MBean

MBeanDescriptor

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE MBean SYSTEM "MbeanDescriptor.dtd">
<MBean type="SampleStateMBean"
  aggregationHandlerClass="com.ibm.ws390.sample.SampleStateAggregationHandler"
  eventHandlerClass="com.ibm.ws390.sample.SampleStateEventHandler"
  invocationHandlerClass="com.ibm.ws390.sample.SampleStateInvocationHandler"
  stateObjectClass="com.ibm.ws390.sample.SampleState"
  version="6.0"
  platform="dynamicproxy"
  description="Sample State MBean for the documentation example.">

  <attribute description="The name of the MBean."
    getMethod="getMBeanName" name="mbeanName" type="java.lang.String"
    proxyInvokeType="unicall"/>
  <attribute description="The state of the MBean." name="state"
    getMethod="getState" setMethod="setState" type="java.lang.String"
    proxyInvokeType="multicall" proxySetterInvokeType="multicall"/>
  <operation
    description="Initialize the State MBean."
    impact="ACTION" name="initializeState" role="operation"
    targetObjectType="objectReference" type="void" proxyInvokeType="multicall">
    <signature>
      <parameter description="The name of the MBean."
        name="mbeanName" type="java.lang.String"/>
      <parameter description="The initial state of the MBean."
        name="mbeanName" type="java.lang.String"/>
    </signature>
  </operation>
  
  <notification name="j2ee.state.starting" severity="6" log="false"
                description="This sample state MBean is in starting state.">
      <notificationType>j2ee.state.starting</notificationType>
  </notification>
  <notification name="j2ee.state.running" severity="6" log="false"
                description="This sample state MBean is in running state.">
      <notificationType>j2ee.state.running</notificationType>
  </notification>
  <notification name="j2ee.state.stopping" severity="6" log="false"
                description="This sample state MBean is in stopping state.">
      <notificationType>j2ee.state.stopping</notificationType>
  </notification>
  <notification name="j2ee.state.stopped" severity="6" log="false"
                description="This sample state MBean is in stopped state.">
      <notificationType>j2ee.state.stopped</notificationType>
  </notification>    
</MBean>

SampleState implementation

package com.ibm.ws390.sample;

import com.ibm.ejs.ras.Tr;
import com.ibm.ejs.ras.TraceComponent;
import java.io.Serializable;
import com.ibm.websphere.management.dynamicproxy.StateObject;

public class SampleState extends StateObject {
    
    private static TraceComponent tc =
    Tr.register(SampleState.class,"SampleState",null);
    
    // Package protected STATE constants.
    static final String STATE_STARTING = "j2ee.state.starting";
    static final String STATE_RUNNING  = "j2ee.state.running";
    static final String STATE_STOPPING = "j2ee.state.stopping";
    static final String STATE_STOPPED  = "j2ee.state.stopped";
    
    // Dynamicproxy State is initialized with STOPPED state.
    private String state = STATE_STOPPED;
    
    public SampleState() {
        if (tc.isEntryEnabled()) Tr.entry(tc,"<init>");
        
        // State is initialized during preceding "state" initialization,
        // but can also be initialized here in the constructor as well.
        /*
        state = "WebSphere Application Server for z/OS ready for e-business";
        */
        
        if (tc.isEntryEnabled()) Tr.exit(tc,"<init>");
    }
    
    public synchronized String getState() {
        if (tc.isEntryEnabled()) Tr.entry(tc,"getState");
        if (tc.isEntryEnabled()) Tr.exit(tc,"getState",state);
        return state;
    }
    
    public synchronized void setState(String state) {
        if (tc.isEntryEnabled()) Tr.entry(tc,"setState",state);
        this.state = state;
        if (tc.isEntryEnabled()) Tr.exit(tc,"setState");
    }
    
    public synchronized String getStateObjectInfo() {
        return state;
    }
}

SampleStateAggregationHandler implementation

package com.ibm.ws390.sample;

import com.ibm.websphere.management.dynamicproxy.AggregationHandler;
import com.ibm.websphere.management.dynamicproxy.StateObject;

import com.ibm.ejs.ras.Tr;
import com.ibm.ejs.ras.TraceComponent;

public class SampleStateAggregationHandler implements AggregationHandler {
    
    private static TraceComponent tc =
    Tr.register(SampleStateAggregationHandler.class,"SampleState",null);
    
    /**
     * Return an aggregated result from a multicall Mbean operation which
     * compiles through all servant MBeans' results and returns a respective
     * single return value for an invoked method.
     *
     * @param  methodName           MBean method name
     * @param  params               MBean method parameters
     * @param  signatures           MBean method signatures
     * @param  servantMBeanResults  Result of each servant MBean instances
     *                              invoked by the dynamicproxy multicast 
     *                              invocation.
     *                              Note: this value can be "null" OR can be
     *                              an array of "null"s in case return value
     *                              of the method is "void." Implementation
     *                              of this method MUST handle this case to
     *                              avoid a <code>NullPointerException</code>.
     * @param stateObject
     *        MBean provider provided <code>StateObject</code> used by 
     *        dynamicproxy MBean in CR to manage its state. Note: this object
     *        MAY BE null if "stateObjectClass" was not specified OR internal
     *        error occurred during initialization of this dynamicproxy MBean.
     *        Implmentation MUST properly handle "null" input.
     *                
     * @return aggregated result as defined by MBean xml for specified
     *         MBean operation.
     */
    public Object aggregateResults(String methodName,
                                   Object[] params,
                                   String[] signatures,
                                   Object[] servantMBeanResults,
                                   StateObject stateObject) {
                                       
        if (tc.isEntryEnabled()) Tr.entry(tc,"aggregateResults",methodName);
        
        // As you can see from the MBeanDescriptor of SampleStateMBean,
        // it declares the following four methods:
        // 1. String getMBeanName()         [proxyInvokeType == unicall]
        // 2. String getState()             [proxyInvokeType == multicall]
        // 3. void setState(String)         [proxyInvokeType == multicall]
        // 4. void initializeState()        [proxyInvokeType == multicall]
        //
        // Looking at the previous methods, only method that requires aggregation
        // is #2 getState method which is a multicall MBean operation AND
        // it returns a value that can be aggregated.
        //
        // In this example, we simply take each servants' getState MBean
        // request result and concatenate them into one long String that
        // displays each servants' state. 
        if (methodName.equals("getState")) {
            StringBuffer stateBuf = new StringBuffer();
            
            for (int i=0; i<servantMBeanResults.length; i++) {
                stateBuf.append("SERVANT #" + i + " state ==|" + 
                                servantMBeanResults[i] + "|== ");
            }
            return stateBuf.toString();
        }
        // If we also had an example method which returns say an int, 
        // getNumberOfMBeans(), it can take the similar approach
        // and to add each servants' getNumberOfMBeans() result together here.
        /* example added for non-existent method: int getNumberOfMBeans()
        else if (methodName.equals("getNumberOfMBeans")) {
            int aggregatedResult = 0;
            
            for (int i=0; i<servantMBeanResults.length; i++) {
                aggregatedResult += (int) servantMBeanResults[i];
            }
            return aggregatedResult;
        }
        */
        
        return methodName + " is NOT handled by " + getClass().getName() + "!";
    }
    
}