Java Dynamic Management Kit 5.1 Tutorial

Part II Agent Applications

The agent is the central component of the Java Management Extensions (JMX) management architecture. An agent contains MBeans and hides their implementation behind a standardized management interface, lets management applications connect and interact with all MBeans, and provides filtering for handling large numbers of MBeans. JMX agents are dynamic because resources can be added and removed, connections can be closed and reopened with a different protocol, and services can be added and removed as management needs evolve.

In Part I, we saw how to represent resources as MBeans. However, MBeans can represent any object whose functionality you need to manage. In particular, management services and remote connectivity are handled by objects that are also MBeans. This creates a homogeneous model where an agent is a framework containing different kinds of MBeans and enabling them to interact.

The main component of an agent is the MBean server. It registers all MBeans in the agent and exposes them for management. The role of the MBean server is to be the liaison between any object available to be managed and any object with a management request. Usually resource MBeans are managed either by remote applications through connectivity MBeans or by local management service MBeans. This model allows a management service itself to be managed. Connectors and services can also be created, modified, or removed dynamically.

This part focuses on the functionality of the MBean server and the Java objects which are needed to create a simple agent. Details about programming managers and about using connectors and services will be covered in Part III.

This part contains the following chapters:

Chapter 5 Base Agent

An agent application is a program written in the Java language that contains an MBean server and a means of accessing its functionality. The base agent demonstrates the programming details of writing an agent application. We will cover the MBean server classes, how to reference MBeans, how to access the MBean server, use it to create MBeans, and then interact with those MBeans.

Interacting programmatically with the MBean server gives you more flexibility in your agent application. This chapter presents the MBean server and covers the three main ways to use it to create MBeans, how to interact with these MBeans, and how to unregister them.

The program listings in this tutorial show only functional code: comments and output statements have been modified or removed to conserve space. However, all management functionality has been retained for the various demonstrations. The complete source code is available in the BaseAgent and StandardMBean example directories located in examplesDir/current (see Directories and Classpath in the Preface).

This chapter covers the following topics:

5.1 MBean Server Classes

Before writing an agent application, it is important to understand the functionality of the MBean server. It is actually an interface and a factory object defined by the agent specification level of the JMX specification. The Java DMK provides an implementation of this interface and factory. The factory object finds or creates the MBean server instance, making it possible to substitute different implementations of the MBean server.

5.1.1 MBeanServer Interface

The MBeanServer interface is an extension of the MBeanServerConnection interface, and represents the agent-side interface for interacting with MBeans.

The specification of the interface defines all operations that can be applied to resources and other agent objects through the MBean server. Its methods can be divided into three main groups:

5.1.2 MBean Server Implementation, Builder and Factory

The MBeanServer implementation class in Java DMK implements the JdmkMBeanServer and MBServerInt interfaces if the property javax.management.builder.initial has been set to use com.sun.jdmk.JdmkMBeanServerBuilder. This implementation of MBeanServer wraps the JMX MBean server.

The older MBeanServerInt interface and the MBeanServerImpl class are still implemented for reasons of backwards compatibility, but in Java DMK 5.1, JdmkMBeanServer is the prefered interface.

The MBeanServerFactory makes the agent application independent of the MBean server implementation, and thus allows applications to provide custom MBeanServer implementations. It resides in the Java virtual machine and centralizes all MBean server instantiation. The MBeanServerFactory class provides two static methods:

You must use the MBeanServerFactory classes to create an MBean server so that other objects can obtain its reference by calling the findMBeanServer method. This method enables dynamically loaded objects to find the MBean server in an agent that has already been started.

5.2 Referencing MBeans

Most agent applications interact with MBeans through the MBean server. It is possible for an object to instantiate an MBean class itself, which it can then register in the MBean server. In this case, it keeps a programmatic reference to the MBean instance. All other objects can only interact with the MBean through its management interface exposed by the MBean server.

In particular, service MBeans and connectivity MBeans rely solely on the MBean server to access resources. The MBean server centralizes the access to all MBeans. It unburdens all other objects from having to keep numerous object references. To ensure this function, the MBean server relies on object names to identify MBean instances uniquely.

5.2.1 Object Names

Each MBean object registered in the MBean server is identified by an object name. The same MBean class can have multiple instances, but each must have a unique name. The ObjectName class encapsulates an object name composed of a domain name and a set of key properties. The object name can be represented as a string in the following format:

DomainName:property=value[,property2=value2]*

The DomainName, the property and value can be any alphanumeric string, as long as it does not contain any of the following characters: : , = * ?. All elements of the object name are treated as literal strings, meaning that they are case sensitive.

5.2.1.1 MBean Domains

A domain is an abstract category that can be used to group MBeans arbitrarily. The MBean server lets you search easily for all MBeans with the same domain. For example, all connectivity MBeans in the minimal server could have been registered into a domain called Communications.

Since all object names must have a domain, the MBeans in an MBean server necessarily define at least one domain. When the domain name is not important, the MBean server provides a default domain name that you can use. By default, it is called the DefaultDomain, but you can specify a different default domain name when creating the MBean server from its factory.

5.2.1.2 Key Properties

A key is a property-value pair that can also have any meaning that you assign to it. An object name must have at least one key. Keys and their values are independent of the MBean's attributes. The object name is a static identifier that identifies the MBean, whereas attributes are the exposed, runtime values of the corresponding resource. Keys are not positional and can be given in any order to identify an MBean.

Keys provide the specificity for identifying a unique MBean instance. For example, an object name for the HTML protocol adaptor MBean might be: Communications:protocol=html,port=8082, assuming the port will not change.

5.2.1.3 Usage of Object Names

All MBeans must be given an object name that is unique. It can be assigned by the MBean's preregistration method, if the MBean supports preregistration (see the Javadoc API of the MBeanRegistration interface). Or it can be assigned by the object that creates or registers the MBean, which overrides the one given during preregistration. However, if neither of these assign an object name, the MBean server will not create the MBean and will raise an exception. Once an MBean is instantiated and registered, its assigned object name cannot be modified.

You can encode any meaning into the domain and key strings. The MBean server handles them as literal strings. The contents of the object name should be determined by your management needs. Keys can be meaningless serial numbers if MBeans are always handled programmatically. On the other hand, the keys can be human-readable to simplify their translation to the graphical user interface of a management application. With the HTML protocol adaptor, object names are displayed directly to the user.

5.2.2 ObjectInstance of an MBean

An object instance represents the complete reference of an MBean in the MBean server. It contains the MBean's object name and its Java class name. Object instances are returned by the MBean server when an MBean is created or in response to queries about MBeans. Since the object name and class name cannot change over the life of a given MBean, its returned object instance will always have the same value.

You cannot modify the class or object name in an object instance. This information is read-only. The object name is used to refer to the MBean instance in any management operation through the MBean server. The class name can be used to instantiate similar MBeans or introspect characteristics of the class.

5.3 Agent Application

The base agent is a standalone application with a main method, but it also has a constructor so that it can be instantiated dynamically by another class.


Example 5–1 Constructor for the Base Agent

public BaseAgent() {

    echo("\n\tInstantiating the MBean server of this agent...");
    mbs = MBeanServerFactory.createMBeanServer();

    // Retrieves ID of the MBean server from
    // the associated MBean server delegate
    //
    echo("\n\tGetting the ID of the MBean server from the " +
             "associated MBean server delegate ...");
    try {
         echo("\tID = " +
             mbs.getAttribute(new ObjectName(ServiceName.DELEGATE),
                              "MBeanServerId"));
    } catch (Exception e) {
        e.printStackTrace();
        System.exit(1);
    }

The MBean server is created through the static MBeanServerFactory object, and we store its object reference. Its true type is hidden by the factory object, that casts the returned object as an MBeanServer interface. The MBean server is the only functional class referenced directly in this application.

After the MBean server is initialized, we will create three communication MBeans.

5.4 Creating an MBean Using the registerMBean Method

The methods of the MBean server enable you to create an MBean in three different ways. The base agent demonstrates all three ways, and we will discuss the advantages of each.

One way of creating an MBean consists of first instantiating its class and then registering this instance in the MBean server. Registration is the internal process of the MBean server that takes a manageable resource's MBean instance and exposes it for management.

Bold text in Example 5–2 and the following code samples highlights the important statements that vary between the three methods.


Example 5–2 Creating an MBean Using the registerMBean Method

// instantiate the HTML protocol adaptor object to use the default port
CommunicatorServer htmlAdaptor = new HtmlAdaptorServer();

try {
    // We let the HTML adaptor provide a default object name
    ObjectInstance htmlAdaptorInstance =
        mbs.registerMBean(htmlAdaptor, null);
    echo("\tCLASS NAME  = " + htmlAdaptorInstance.getClassName());
    echo("\tOBJECT NAME = " + 
    htmlAdaptorInstance.getObjectName().toString());
} catch(Exception e) {
    e.printStackTrace();
    System.exit(1);
}
htmlAdaptor.start();

// Waiting to leave starting state...    
while (htmlAdaptor.getState() == CommunicatorServer.STARTING) {
    sleep(1000);
}
echo("\tSTATE = " + htmlAdaptor.getStateString());
[...]

In this first case, we instantiate the HtmlAdaptorServer class and keep a reference to this object. We then pass it to the registerMBean method of the MBean server to make our instance manageable in the agent. During the registration, the instance can also obtain a reference to the MBean server, something it requires to function as a protocol adaptor.

The HTML adaptor gives itself a default name in the default domain. Its Javadoc API confirms this, so we can safely let it provide a default name. We print the object name in the object instance returned by the registration to confirm that the default was used.

Once the MBean is registered, we can perform management operations on it. Because we kept a reference to the instance, we do not need to go through the MBean server to manage this MBean. This enables us to call the start and getStateString methods directly. The fact that these methods are publicly exposed is particular to the implementation. The HTML adaptor is a dynamic MBean, so without any prior knowledge of the class, we would have to go through its DynamicMBean interface.

In a standard MBean you would call the implementation of its management interface directly. Because the HTML adaptor is a dynamic MBean, the start method is just a shortcut for the start operation. For example, we could start the adaptor and get its StateString attribute with the following calls:

htmlAdaptor.invoke("start", new Object[0], new String[0]);
echo("STATE = " + (String)htmlAdaptor.getAttribute("StateString"));

This type of shortcut is not specified by the Java Management Extensions (JMX) specification, nor is its functionality necessarily identical to that of the start operation exposed for management. In the case of the HTML adaptor, its Javadoc API confirms that it is identical, and in other cases, it is up to the MBean programmer to guarantee this functionality if it is offered.

5.5 Creating an MBean Using the createMBean Method

A second way to create an MBean is the single createMBean method of the MBean server. In this case, the MBean server instantiates the class and registers it all at once. As a result, the caller never has a direct reference to the new object. This is not demonstrated in the BaseAgent example, but the advantage of this method for creating MBeans is that the instantiation and registration are done in one call. In addition, if we have registered any class loaders in the MBean server, they will automatically be used if the class is not available locally. See Chapter 12, M-Let Class Loader for more information on class loading.

The major difference is that we no longer have a reference to our MBean instance. The object instance that was only used for display purposes in the previous example now gives us the only reference we have on the MBean: its object name.

One disadvantage of this method is that all management of the new MBean must now be done through the MBean server. For the attributes of the MBean, we need to call the generic getter and setter of the MBean server, and for the operations we need to call the invoke method. When the agent needs to access the MBean, having to go through the MBean server adds some complexity to the code. However, it does not rely on any shortcuts provided by the MBean, making the code more portable and reusable.

The createMBean method is ideal for quickly starting new MBeans that the agent application does not need to manipulate. In just one call, the new objects are instantiated and exposed for management.

5.6 Creating an MBean Using the instantiate Method

The last way of creating an MBean relies on the instantiate method of the MBean server. In addition, we can use a nondefault constructor to instantiate the class with a different behavior. This method is not demonstrated in the BaseAgent example, but as in Example 5–2, we instantiate and register the MBean in separate steps.

First, you instantiate the class using the instantiate method of the MBean server. This method lets you specify the constructor you want use when instantiating. Note that we could also have specified a constructor to the createMBean method in the previous example.

To specify a constructor, you must give an array of objects for the parameters and an array of strings that defines the signature. If these arrays are empty or null, the MBean server will try to use the default no-parameter constructor. If the class does not have a public no-parameter constructor, you must specify the parameters and signature of a valid public constructor.

One advantage of this creation method is that the instantiate method of the MBean server also supports class loaders. If any are registered in the MBean server, they will automatically be used if the class is not available locally. See Chapter 12, M-Let Class Loader for more information on class loading.

The main advantage of this method is that, like the first method of MBean creation, we retain a direct reference to the new object. The direct reference again enables us to use the MBean's shortcut methods explicitly.

5.7 Managing MBeans

In 5.5 Creating an MBean Using the createMBean Method, you rely totally on the MBean server to create and access an MBean. You get attributes and invoke operations through the MBean server. Here we will concentrate on the usage of MBean metadata classes when accessing MBeans that represent resources.

We will rely on the StandardAgent and DynamicAgent classes presented in Part I. As mentioned in 2.3.1 Comparison With the SimpleStandard Example, the two are nearly identical. The same code works for any registered MBean, whether standard or dynamic. We examine the method for displaying MBean metadata that is common to both.


Example 5–3 Processing MBean Information

private MBeanServer server = null; // assigned by MBeanServerFactory

private void printMBeanInfo(ObjectName mbeanObjectName, String mbeanName) {

    echo("    using the getMBeanInfo method of the MBeanServer");
    sleep(1000);
    MBeanInfo info = null;
    try {
         info = server.getMBeanInfo(mbeanObjectName);
    } catch (Exception e) {
          echo("\t!!! Could not get MBeanInfo object for " +
               mbeanName + " !!!");
          e.printStackTrace();
          return;
    }
    echo("\nCLASSNAME: \t"+ info.getClassName());
    echo("\nDESCRIPTION: \t"+ info.getDescription());
    echo("\nATTRIBUTES");
    MBeanAttributeInfo[] attrInfo = info.getAttributes();
    if (attrInfo.length>0) {
        for(int i=0; i<attrInfo.length; i++) {
            echo(" ** NAME: \t"+ attrInfo[i].getName());
            echo("    DESCR: \t"+ attrInfo[i].getDescription());
            echo("    TYPE: \t"+ attrInfo[i].getType() +
                 "\tREAD: "+ attrInfo[i].isReadable() +
                 "\tWRITE: "+ attrInfo[i].isWritable());
        }
    } else echo(" ** No attributes **");

    echo("\nCONSTRUCTORS");
    MBeanConstructorInfo[] constrInfo = info.getConstructors();
    for(int i=0; i<constrInfo.length; i++) {
        echo(" ** NAME: \t"+ constrInfo[i].getName());
        echo("    DESCR: \t"+ constrInfo[i].getDescription());
        echo("    PARAM: \t"+ constrInfo[i].getSignature().length +
                 " parameter(s)");
    }

    echo("\nOPERATIONS");
    MBeanOperationInfo[] opInfo = info.getOperations();
    if (opInfo.length>0) {
        for(int i=0; i<constrInfo.length; i++) {
            echo(" ** NAME: \t"+ opInfo[i].getName());
            echo("    DESCR: \t"+ opInfo[i].getDescription());
            echo("    PARAM: \t"+ opInfo[i].getSignature().length +
                     " parameter(s)");
        }
    } else echo(" ** No operations ** ");

    echo("\nNOTIFICATIONS");
    MBeanNotificationInfo[] notifInfo = info.getNotifications();
    if (notifInfo.length>0) {
        for(int i=0; i<constrInfo.length; i++) {
            echo(" ** NAME: \t"+ notifInfo[i].getName());
            echo("    DESCR: \t"+ notifInfo[i].getDescription());
            String notifTypes[] = notifInfo[i].getNotifTypes();
            for (int j = 0; j < notifTypes.length; j++) {
                echo("    TYPE: \t" + notifTypes[j]);
            }
        }
    } else echo(" ** No notifications **");
}

The getMBeanInfo method of the MBean server gets the metadata of an MBean's management interface and hides the MBean's implementation. This method returns an MBeanInfo object that contains the MBean's description. We can then get the lists of attributes, operations, constructors, and notifications to display their descriptions. Recall that the dynamic MBean provides its own meaningful descriptions and that the standard MBean descriptions are default strings provided by the introspection mechanism of the MBean server.

5.8 Filtering MBeans With the Base Agent

The base agent does very little filtering because it does very little management. Usually, filters are applied programmatically to get a list of MBeans to which some operations apply. There are no management operations in the MBean server that apply to a list of MBeans. You must loop through your list and apply the desired operation to each MBean.

Before exiting the agent application, we do a query of all MBeans so that we can unregister them properly. MBeans should be unregistered before being destroyed because they might need to perform some actions before or after being unregistered. See the Javadoc API of the MBeanRegistration interface for more information.


Example 5–4 Unregistering MBeans

public void removeMBeans() {
        
        try {
            echo("Unregistering all the registered MBeans except " +
                 "the MBean server delegate\n");
            echo("    Current MBean count = " + mbs.getMBeanCount() 
                 + "\n");
            Set allMBeans = mbs.queryNames(null, null);
            for (Iterator i = allMBeans.iterator(); i.hasNext(); ) {
                ObjectName name = (ObjectName) i.next();
                if (!name.toString().equals(ServiceName.DELEGATE)) {
                    echo("\tUnregistering " + name.toString());
                    mbs.unregisterMBean(name);
                }
            }
            echo("\n    Current MBean count = " + mbs.getMBeanCount() 
                +"\n");
            echo("done\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

We use the queryNames method because we only need the object names to operate on MBeans. The null object name as a filter gives us all MBeans in the MBean server. We then iterate through the resulting set and unregister each one, except for the MBean server delegate. As we shall see in 6.1.2 MBean Server Delegate, the delegate is also an MBean and so it will be returned by the query. However, if we unregister it, the MBean server can no longer function and cannot remove the rest of our MBeans.

We recognized the delegate by its standard name which is given by the static field ServiceName.DELEGATE. The ServiceName class provides standard names and other default properties for communications and service MBeans. It also provides the version strings that are exposed by the delegate MBean. Note that, because the delegate is the only MBean created directly by the MBean server, it is the only one whose name cannot be overridden during its registration. The delegate object name is always the same, so we are always sure to detect it.

5.9 Running the Base Agent Example

The examplesDir/current/BaseAgent/ directory contains the source file of the BaseAgent application.

To Run the Base Agent Example
  1. Compile the BaseAgent.java file in this directory with the javac command.

    For example, on the Solaris platform, type:


    $ cd examplesDir/current/BaseAgent/
    $ javac -classpath classpath *.java
    

    Again, we do not need the MBean classes at compile time, but they will be needed at runtime, because we do not use a dynamic class loader. You will need to have compiled the standard and dynamic MBean classes as described in 1.3 Running the Standard MBean Example and 2.3 Running the Dynamic MBean Example. If you want to load any other class in the base agent, you must include its directory or JAR file in the classpath.

  2. To run the example, update your classpath to find the MBeans and start the agent class:


    $ java -classpath classpath:../StandardMBean:../DynamicMBean BaseAgent
    

5.9.1 Agent Output

This agent displays output for the MBean created.

When the connection MBeans have been created, it is possible to connect to the agent through the HTML adaptor.

When you have finished, press Enter to remove all MBeans from the agent and exit the agent application.

Chapter 6 HTML Protocol Adaptor

The HTML protocol adaptor provides a view of the agent and its registered MBeans through a basic interface on any web browser. It is the easiest way to access an agent since no further coding is necessary. For this reason, it can be useful for testing and debugging your MBeans.

In this chapter, we will use your browser to “manage” the base agent and its MBeans. The HTML protocol adaptor outputs HTML pages that represent the agent and its MBeans. The adaptor also interprets the commands sent back by the buttons and fields appearing in your browser. It then interacts with the agent's MBean server to get information about the MBeans that it has registered and to operate on them.

The HTML adaptor relies mostly on plain HTML. The only JavaScriptTM that the generated pages contain are pop-up windows for displaying information. Browsers that are not enabled for JavaScript might give an incompatibility message and will not be able to display the information. Otherwise, the generated pages contain no further scripting (JavaScript, Visual Basic or other), no frames and no images that might slow down loading.

This chapter relies on the base agent that you will need to start first, as explained in Chapter 5, Base Agent. Once you can connect to the HTML protocol adaptor in the base agent, you are ready to begin these topics:

6.1 Agent View

The first page displayed by the HTML adaptor is always the agent view. It initially contains a list of all registered MBeans. The following figure shows the agent view for the base agent. It contains four MBeans: three communication MBeans, one of which is the HTML adaptor, and the MBean server delegate. The delegate is a special MBean explained in 6.1.2 MBean Server Delegate.

Figure 6–1 Initial Agent View of the Agent

Screen shot showing the initial agent view of the agent

The text field for filtering by object name enables you to modify the list of displayed MBeans. The filter string is initially *:*, which displays all registered MBeans. 6.3.2 Filtering MBeans describes how to use the filter. The agent's registered domain indicates the name of the default domain in this agent. The number of MBeans on this page is the count of those listed beneath the separator line.

The Admin button links to the agent administration page (see 6.3 Agent Administration).

6.1.1 MBean List

The MBean list contains all MBeans whose object name matches the filter string. Object names can be filtered by their domain name and list of key-value pairs. In this list, MBeans are sorted and grouped by domain name. Each MBean name listed is an active link to the page of the corresponding MBean view.

After its initialization, the contents of an agent are dynamic. New MBeans can be created and registered into new or existing domains and old MBeans can be removed. These changes can also affect the functionality of the agent. New agent services can be registered (or removed) as well. We will demonstrate examples of dynamic management in 6.3.1 Instantiating and Managing MBeans.

6.1.2 MBean Server Delegate

The MBean server delegate is an MBean that is automatically instantiated and registered by the MBean server when it is created. It provides information about the version of the Java Dynamic Management Kit (Java DMK) that is running, and it represents the MBean server when sending notifications.

Notifications are events sent by MBeans. They are covered in detail in Chapter 8, Notification Mechanism. Because the MBean server instance is not an MBean object, it relies on its delegate MBean to send notifications. The MBean server delegate sends notifications to inform interested listeners about such events as MBean registrations and unregistrations.

The exposed attributes of the delegate MBean provide vendor and version information about the MBean server. This can let a remote management application know which agent version is running and which version of the Java runtime environment it is using. The delegate MBean also provides a unique identification string for its MBean server.

To View the MBean Server Delegate Information
  1. Click the name of the delegate MBean to see its attributes.

    Version, vendor and identification information is listed in the table of attributes.

  2. Click the Back to Agent View link or use your browser's “Previous page” function to return to the MBean list in the agent view.

6.2 MBean View

The MBean view has two functions: it presents the management interface of the MBean and it enables you to interact with its instance. The management interface of an MBean is given through the name of the attributes, the operation signatures, and a self-description. You can interact with the MBean by reloading its attribute values, setting new values, or invoking an operation.

To Display the MBean View
  1. Display the agent view.

    This is the first page displayed by the HTML adaptor.

  2. In the agent view, click on the object name of the HTML adaptor MBean: name=HTMLAdaptorServer in the default domain.

    This will display its MBean view.

6.2.1 Header and Description

As shown in Figure 6–2, the top part of the page contains the description of the MBean and some controls for managing it.

Figure 6–2 Description in the MBean View

Screen shot showing description of the MBean

The first two lines give the object instance (object name and class name) for this MBean. The MBean name is the full object name of this MBean instance, including the domain. The key-property pairs might or might not identify the MBean to a human reader, depending on the agent developer's intention. The MBean Java class is the full class name for the Java object of which this MBean is an instance.

The reload controls include a text field for entering a reload period and a manual Reload button. Initially, the reload period is set to zero indicating that the contents of the MBean view are not automatically refreshed. Clicking the Reload button forces the page to reload, thereby updating all of the attribute values displayed. If you have entered a reload period, clicking the button will begin automatic reloading with the given period. The reload period must be at least five seconds.


Note –

Use the Reload button of the MBean view instead of the browser's reload page button. After some operations, such as applying changes to attribute values, the browser's button will repost the form data, inadvertently performing the same operation again. To avoid undesirable side effects, always use the Reload button provided in the MBean view.


To Set the Reload Period
  1. Type a reload period of 5 and click the Reload button.

    Every five seconds the page will blink as it reloads.

  2. In another browser window, open another connection to the HTML adaptor at http://localhost:8082/.

  3. Observe the new values for the ActiveClientCount and LastConnectedClient attributes in the original window.

    You might have to try several connections before you see the attribute values change.

The reload period is reset to zero every time you open an MBean view.

The Unregister button is a shortcut for removing this MBean from the agent. Unregistering is covered in 6.3.1 Instantiating and Managing MBeans.

The MBean description text provides some information about the MBean. Because standard MBeans are statically defined, they cannot describe themselves and the MBean server provides a generic text. Dynamic MBeans are required to provide their own description string at runtime according to the JMX specification. Except for the class name, this is the only way to tell standard and dynamic MBeans apart in the MBean view.

6.2.2 Table of Attributes

The second part of the MBean view is a table containing all attributes exposed by the MBean. For each attribute, this table lists its name, its Java type, its read-write access, and a string representation of its current value.

While MBean attributes can be of any type, not all types can be displayed in the MBean view. The HTML adaptor is limited to basic data types that can be displayed and entered as strings. Read-only attributes whose type supports the toString method are also displayed. Enumerated types that are concrete subclasses of com.sun.jdmk.Enumerated are displayed as a menu with a pop-up selection list. Boolean attributes are represented as true-false radio buttons. Finally, attributes with array types are represented by a link to a page that displays the array values in a table. If the attribute is writable, you can enter values for the array elements to set them.

For the complete list of supported types, see the Javadoc API of the HtmlAdaptorServer class. If an attribute type is not supported, this is indicated in place of its value. If there was an error when reading an attribute's value, the table shows the name of the exception that was raised and the message it contains.

The name of each attribute is a link that displays a window containing the description for this attribute. Like the MBean description, attribute descriptions can only be provided by dynamic MBeans. The MBean server inserts a generic description for standard MBean attributes. Figure 6–3 shows the attributes of the HTML adaptor with a description of the Active attribute.

Figure 6–3 MBean Attributes With a Description Window

Screen shot showing the attributes of the HTML adaptor and a description window for the Active attribute

To View an Attribute Description
  1. Click on an attribute name in the table of attributes to read its description.

    Because the HTML adaptor is implemented as a dynamic MBean, its attribute descriptions are meaningful.


    Note –

    Due to the use of JavaScript commands in the generated HTML, these pop-up windows might not be available on browsers that are not JavaScript-enabled.


Writable attributes have a text field for entering new values. To set the value of a writable attribute, type or replace its current value in the text field and click the Apply button at the bottom of the attributes table.


Note –

Do not try to modify the attributes of the HTML protocol adaptor here. See 6.3.1 Instantiating and Managing MBeans.


Because there is only one Apply button for all the attributes, this systematically invokes the setter for all writable attributes, whether or not their fields have actually been modified. This might affect the MBean if setters have side effects, such as counting the number of modifications, as in the SimpleStandard and SimpleDynamic examples given in Part I

The HTML adaptor detects attributes of the ObjectName type and provides a link to the view of the corresponding MBean. This link is labeled view and is located just under the displayed value of the object name. Because MBeans often need to reference other MBeans, this provides a quick way of navigating through MBean hierarchies.

6.2.3 List of Operations

The last part of the MBean view contains all the operations exposed by the MBean. Each operation in the list is presented like a method signature. There is a return type, then a button with the operation name, and if applicable, a list of parameters, each with their type as well.

As with the table of attributes, the list of operations contains only those involving types that can be represented as strings. The return type must support the toString method and the type of each parameter must be one of basic data types supported by the HTML adaptor. For the complete list, see the Javadoc API of the HtmlAdaptorServer class.

Above each operation name is a link to its description. Parameter names are also active links that display a window with a description. Again, descriptions are only meaningful when provided by dynamic MBeans. The following figure shows some of the operations exposed by the HTML adaptor MBean and a description of the Start operation.

Figure 6–4 MBean Operations With a Description Window (Partial View)

Screen shot giving a partial view of MBean operations, with a description window

We will not perform any operations on this MBean until after a brief explanation in 6.3.1 Instantiating and Managing MBeans.

To perform an operation, fill in any and all parameter values in the corresponding text fields and click the operation's button. The HTML adaptor displays a page with the result of the operation, either the return value if successful or the reason why the operation was unsuccessful.

6.3 Agent Administration

The agent administration page contains a form for entering MBean information when creating or unregistering MBeans. You can also instantiate an MBean through one of its public constructors. In order to instantiate an MBean, its class must be available in the agent application's classpath. Optionally, you can specify a different class loader if the agent contains other class loader MBeans.

To Display the Agent Administration Page
  1. Go back to the agent view by clicking the link near the top of the MBean view page.

  2. Click on the Admin button in the agent view to display the agent administration page in your browser window.

The first two fields, Domain and Keys are mandatory for all administrative actions. The Domain field initially contains the string representing the agent's default domain. Together, these two fields define the object name, whether for a new MBean to be created or for an existing MBean to unregister. The Java class is the full class name of the object to be instantiated as a new MBean. This field is ignored when unregistering an MBean.

Using the pull-down menu, you can choose one of three actions:

When you click the Send Request button, the HTML adaptor processes the action and updates the bottom of the page with the action results. You might have to scroll down to see the result. The text fields are not cleared after a request, enabling you to perform multiple operations. Clicking the Reset button returns the fields to their last posted value after you have modified them.

6.3.1 Instantiating and Managing MBeans

Sometimes, starting an MBean requires several steps, especially for agent services that require some sort of configuration. For example, you can instantiate another HTML adaptor for connecting to a different port. Usually, this is done programmatically in the agent application, but we must do it through the browser for the agent.

To Create a New HTML Adaptor MBean
  1. On the agent administration page, fill in the fields as follows:

    Domain

    Communications

    Keys:

    protocol=html,port=8088

    Java Class:

    com.sun.jdmk.comm.HtmlAdaptorServer

    Class Loader:

    leave blank

    In versions of the Java DMK prior to version 4.0, specifying the port number in the object name would initialize communication MBeans. Now, the names and contents of key properties no longer have any significance for any components of the product. We must set the port in other ways.

  2. Make sure the action selected is Create and send the request. If you scroll down the page, you will see whether your request was successful.

    We cannot connect to this HTML adaptor quite yet. First we need to configure it .

  3. Go to the new HTML adaptor's MBean view with the provided link.

    Remember that we could not modify any of the adaptor's attributes in To View an Attribute Description because the implementation does not allow them to be modified while the HTML adaptor is online. Our new HTML adaptor is instantiated in the stopped state (the StateString attribute indicates OFFLINE), so we can change its attributes.

  4. Set the Port attribute to 8088 and MaxActiveClientCount to 2, then click the Apply button.

    If the page is reloaded and the new values are displayed, the attribute write operation was successful. You can also click the attribute names to get a description them.

  5. Scroll down the MBean view to the Start operation and click the Start button.

    A new page is displayed to tell us the operation was successful. If you go back to the MBean view with the provided link, you can see that the StateString is now indicating ONLINE.

  6. Now you can access your agent through a browser on port 8088. Try going to a different host on the same network and connecting to the following URL:

    http://agentHostName:8088/

    In the above URL, agentHostName is the name or IP address of the host where you launched the BaseAgent. If you reload the MBean view of the new HTML adaptor on either browser, the name of this other host should be the new value of the LastConnectedClient attribute.

Through this new connection, you can stop, modify or remove the HTML adaptor MBean using port 8082. In that case, your original browser will also have to use http://localhost:8088/ to connect. Instead, we will manage the agent from the second host.

To Instantiate MBeans With Constructors
  1. From the browser on the second host, go to the agent administration page. Fill in the fields as follows and request the constructors:

    Domain:

    Standard_MBeans

    Keys:

    name=SimpleStandard,number=1

    Java Class:

    SimpleStandard

    Class Loader:

    leave blank

    The list of constructors for the SimpleStandard class is displayed at the bottom of the page. The MBean name is also shown. This is the object name that will be assigned to the MBean when using one of the listed constructors. As you can see, the SimpleStandard class only has one constructor that takes no parameters.

  2. Click on the Create button. The result is appended to the bottom of the page. Scroll down and go to the MBean view with the provided link.

    Because it is a standard MBean, all of its description strings are generic. This shows the necessity of programming meaningful attribute names.

  3. In the agent view on the first browser window, click in the filter field and press Return to refresh the agent view.

  4. Click the new MBean's name and set its reload period to 15.

  5. On the second host, type in a different string for the State attribute and click Apply.

    On the first host, you should see the MBean's attributes are updated when the MBean view is periodically reloaded.

  6. On the second host, click the Reset operation button at the bottom of the MBean view page.

    The operation result page is displayed and indicates the success of the operation.

    This page also provides the return value of the operation when it is not void. If you go back to the MBean view, you will see the result of the operation on the attributes. You should also see it on the first host after it reloads.

The browser on the second host is no longer needed and we can remove the HTML adaptor on port 8088.

To Unregister an MBean
  1. Go to the administration page and fill in the object name of the HTML adaptor you want to remove (you do not need its Java class to unregister it):

    Domain:

    Communications

    Keys:

    protocol=html,port=8088

    Java Class:

    leave blank

    Class Loader:

    leave blank

  2. Choose Unregister from the pull-down menu and click the Send Request button.

    The result is displayed at the bottom of the page.

    You can also unregister an MBean directly from its MBean view. Click the Unregister button on the upper right side of the page.

6.3.2 Filtering MBeans

Because an agent can manage hundreds of MBeans, the agent view provides a filtering mechanism for the list that is displayed. An object name with wildcard characters is used as the filter, and only those MBeans that match are counted and displayed.

Filters restrict the set of MBeans listed in the agent view. This might not be particularly useful for our small agent, but it can help you find MBeans among hundreds in a complex agent. In addition, management applications use the same filter syntax when requesting an agent's MBeans through the programmatic interface of a connector. The filtering enables managers to either get lists of MBean names or find a particular MBean instance.

Filters are typed as partial object names with wildcard characters or as a full object name for which to search, using the syntax domain:key. Here are the basic substitution rules for filtering.

  1. You can search for partial domain names, using the following characters:

    Asterisk (*)

    Replaces any number (including zero) of characters

    Question mark (?)

    Replaces any one character

  2. An empty domain name is replaced by the default domain string; an empty key list is illegal

  3. Keys are atomic; you must search for the full property=value key; you cannot search for a partial property name or an incomplete value

  4. An asterisk (*) can be used to terminate the key list, where it replaces any number of any keys (complete property-value pairs)

  5. You must match all keys exactly; use the form property=value,* to search for one key in names with multiple keys

  6. Keys are unordered when filtering; giving one or more keys (and an asterisk) in any order finds all object names that contain that subset of keys

To Filter MBeans
  1. Go to the administration page and create three more standard MBeans. Modify only the number value in their object names so that they are numbered sequentially.

  2. In the same way as Step 1, create four dynamic MBeans starting with:

    Domain:

    Dynamic_MBeans

    Keys:

    name=SimpleDynamic,number=1

    Java Class:

    SimpleDynamic

    Class Loader:

    leave blank

  3. Go back to the agent view, that should display all the new MBeans.

  4. Type each of the filter strings given in Table 6–1 to see the resulting MBean list

    Table 6–1 Examples of Filter Strings

    Filter String (domain:key)

    Result 

    Standard_MBeans:*

    Gives all of the standard MBeans we created 

    *_MBeans:*

    Gives all of the standard and dynamic MBeans we created 

    DefaultDomain:

    Not allowed by rule 2 

    :*

    Lists all MBeans in the default domain 

    *:name=Simple*,*

    Not allowed by rule 3 

    *:name=SimpleStandard

    Allowed, but list is empty (rule 5) 

    *:name=*

    Not allowed by rule 3 

    *_??????:number=2,*

    Gives the second standard and dynamic MBean we created 

    Communications:port=8088,protocol=html

    Gives the one MBean matching the domain and both (unordered) keys  

    empty string

    Allowed: special case equivalent to *:*

    Notice how the MBean count is updated with each filter. This count shows the number of MBeans that were found with the current filter, which is the number of MBeans appearing on the page. It is not the total number of MBeans in the agent, unless the filter is *:*.

  5. When you are ready to stop the base agent example, go to the window where you started its class and press Control-C.

Chapter 7 MBean Server Interceptors

An MBean server forwards requests it receives to its default interceptor. A feature of the Java Dynamic Management Kit (Java DMK) enables you to modify this behavior of the MBean server and replace the default interceptor by another object implementing the same interface.

The example provided demonstrates how you can use the concept of MBean server interceptors to forward requests to a specific interceptor, and to support “virtual” MBeans. The code samples in this chapter are from the files in the MBeanServerInterceptor example directory located in the main examplesDir/current directory (see “Directories and Classpath” in the Preface).

This chapter covers the following topics:

7.1 Overview of MBean Server Interceptors

The concept of interceptors exploits the proxy design pattern to enable you to modify the behavior of the MBean server. By default, the MBean server appears from the outside like a hollow shell that simply forwards every operation to the default interceptor. You can replace this default interceptor by another object implementing the same interface, to change the semantics of the MBean server. In most cases, you would use this other object to forward most or all operations to the default interceptor after doing some processing. However, you can also use it to forward some operations to other handlers instead, for instance depending on the object names involved. Figure 7–1 shows schematically how you can insert an interceptor between the MBean server and the default interceptor.

Figure 7–1 Inserting a User Interceptor Between the MBean Server and the Default Interceptor

Diagram showing that a user interceptor is inserted between the MBean server and the default interceptor

Some examples of the uses of interceptors are as follows:

Interceptors can be composed. When an interceptor is added, it is usually inserted between the MBean server shell and the current interceptor. Initially, the current interceptor is the default one. But if another interceptor has already been inserted, this other interceptor is the current one. Hence, a request could pass through several interceptors on its way to the default interceptor, for example a security checker and a logger.

7.2 Specifying the Behavior of an MBean Server Interceptor

The behavior to be implemented by an interceptor is specified by means of the MBeanServerInterceptor interface. An MBean server interceptor has essentially the same interface as an MBean server. An MBean server forwards received requests to its default interceptor, which might handle them itself or forward them to other interceptors.

7.3 Changing the Default Interceptor

The default interceptor can be changed by using the setMBeanServerInterceptor method of the JdmkMBeanServer interface. The JdmkMBeanServer interface provides methods for getting and setting the default MBeanServerInterceptor used for request treatment.

By default, the MBeanServer implementation returned by the MBeanServerFactory is not a JdmkMBeanServer, and does not support MBeanServerInterceptors. To use interceptor you have to set the javax.management.builder.initial System property to com.sun.jdmk.JdmkMBeanServerBuilder before obtaining an MBean server from the MBeanServerFactory. How to set this system property is shown in Step 2.


Note –

Particular care must be taken when replacing the default MBean server interceptor with a user interceptor. The MBean server implemented in the Java DMK 5.1 passes requests to its default interceptor without checking the result returned, or the exceptions thrown by the interceptor.

Consequently, user interceptors, which implement most of the methods defined in the MBeanServer interface, must behave as specified for the corresponding MBeanServer methods in the JMX specification. In particular, a method in an MBean server interceptor must not throw any exceptions apart from the following:

If an MBean server interceptor does not respect this condition, and, for example, throws a NullPointerException exception, this might have unexpected effects on calling code, which might not be protected against such behavior.


7.4 Running the MBean Server Interceptor Example

The MBean server interceptor example in the examples directory shows you two of the main functions of MBean server interceptors, forwarding requests to a specific MBean server interceptor, and creating virtual MBeans.

The examplesDir/current/MBeanServerInterceptor directory contains the following source files:

To Run the MBean Server Interceptor Example
  1. Compile all files in the examplesDir/current/MBeanServerInterceptor directory with the javac command.

    For example, on the Solaris platform, type:


    $ cd examplesDir/current/MBeanServerInterceptor/
    $ javac -classpath classpath *.java
    
  2. Run the example using the classes you have just built, by typing the following command in a terminal window:


    $ java \
    -Djavax.management.builder.initial=com.sun.jdmk.JdmkMBeanServerBuilder \
    Agent
    

    Here, you can see that the javax.management.builder.initial system property is set to com.sun.jdmk.JdmkMBeanServerBuilder before the Agent is started, as explained in 7.3 Changing the Default Interceptor.

  3. Interact with the agent through the standard input and output in the window where it was started.

  4. Load the agent's URL in your web browser:


    http://localhost:8082/

    You only see the MBeans registered in the DefaultMBeanInterceptor, namely the connector and adaptor MBeans, and the MBeanServerDelegate.

  5. Press Enter to insert the FileMBeanServerInterceptor and view the files from the local directory as virtual MBeans.

  6. Reload the agent's URL in your web browser to view the new MBeans:


    http://localhost:8082/
  7. Press Enter to stop the agent.

Chapter 8 Notification Mechanism

This chapter presents the mechanisms for sending and receiving notifications by demonstrating them locally on the agent-side. MBeans for either resources or services are the source of notifications, called broadcasters. Other MBeans or objects that want to receive the notifications register with one or more broadcasters, and they are called listeners.

Notification mechanisms are demonstrated through two sample broadcasters. The MBean server delegate that notifies listeners of MBean creation and unregistration, and an MBean that sends attribute change notifications.

The code samples are from the files in the examplesDir/current/Notification example directory located in the main examplesDir (see Directories and Classpath in the Preface).

This chapter covers the following topics:

8.1 Overview of Notifications

The ability for resources and other entities to signal an event to their managing applications is a key functionality of management architectures. As defined in the Java Management Extensions (JMX) specification, notifications in the Java Dynamic Management Kit (Java DMK) provide a generic event mechanism whereby a listener can receive all events sent by a broadcaster.

All notifications in the Java DMK rely on the Notification class that itself inherits from Java event classes. A string called the notification type inside a Notification object gives the nature of the event, and other fields provide additional information to the recipient. This ensures that all MBeans, the MBean server, and remote applications can send and receive Notification objects and its subclasses, regardless of their inner type.

You can define new notification objects only by subclassing the Notification class. This ensures that the custom notifications will be compatible with the notification mechanism. New notification classes can be used to convey additional information to custom listeners, and generic listeners will still be able to access the standard Notification fields. However, because there are already fields provided for user data, subclassing is discouraged in the JMX architecture so that notification objects remain as universal as possible.

Listeners usually interact with notification broadcasters indirectly through the MBean server. The interface of the MBean server enables you to associate a listener with any broadcaster MBean, thereby giving you dynamic access to any of the broadcasters that are registered. In addition, the MBean metadata provided through the MBean server contains the list of notification types that the MBean broadcasts.

Figure 8–1 summarizes how listeners register with broadcasters and then receive notifications, in an agent application.

Figure 8–1 Listener Registration and Notification Propagation

Diagram showing how listeners register with broadcasters and receive notifications

8.2 MBean Server Delegate Notifications

The MBean server delegate object is an MBean that is automatically created and registered when an MBean server is started. It preserves the management model by serving as the management interface for the MBean server. The delegate exposes read-only information such as the vendor and version number of the MBean server. More importantly for this chapter, it sends the notifications that relate to events in the MBean server: all MBean registrations and unregistrations generate a notification.

8.2.1 NotificationEmitter Interface

A class must implement the NotificationEmitter interface to be recognized as a source of notifications in the JMX architecture. This interface provides the methods for adding or removing a notification listener to or from the emitter. When the emitter sends a notification, it must send it to all listeners that are currently registered through this interface.

This interface also specifies a method that returns information about all notifications that might be sent by the emitter. This method returns an array of MBeanNotificationInfo objects, each of which provides a name, a description string, and the type string of the notification.

As detailed in the Javadoc API, the MBeanServerDelegate class implements the NotificationEmitter interface. We know from the JMX specification that it sends notifications of the following types:


Note –

Although emitter objects are almost always MBeans, they should not expose the methods of the NotificationEmitter interface. That is, the MBean interface of a standard MBean should never extend the NotificationEmitter interface. As we shall see in Chapter 22, Notification Forwarding in Legacy Connectors, the remote connector clients provide the methods needed to register for and receive notifications remotely.


The NotificationEmitter should be used in preference to the old NotificationBroadcaster class.

8.2.2 NotificationListener Interface

Listeners must implement the NotificationListener interface, and they are registered in the notification broadcasters to receive the notifications. The listener interface defines a handler method that receives all notifications of the broadcaster where the listener is registered. We say that a listener is registered when it has been added to the broadcaster's list of notification recipients. This is completely independent of any registration of either object in the MBean server.

Like the broadcaster, the listener is generic, meaning that it can handle any number of different notifications. Its algorithm usually involves determining the type of the notification and taking the appropriate action. A listener can even be registered with several broadcasters and handle all of the notifications that are sent.


Note –

The handler is a callback method that the broadcaster calls with the notification object it wants to send. As such, it runs in the broadcaster's thread and should therefore run rapidly and return promptly. The code of the handler should rely on other threads to execute long computations or blocking operations.


In our example, the listener is a trivial class that has a constructor and the handler method. Our handler simply prints out the nature of the notification and the name of the MBean to which it applied. Other listeners on the agent side might themselves be MBeans that process the event and update the state of their resource or the quality of their service in response. For example, the relation service must know when any MBeans participating in relations are unregistered. It does this by listening to MBean server delegate notifications.


Example 8–1 Listener for MBean Server Delegate Notifications

import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.MBeanServerNotification;

public class AgentListener implements NotificationListener {

         public AgentListener() {
         }    

         public void handleNotification(Notification n,
                                   Object h) {

        // Process the different types of notifications fired by the
        // MBean server delegate.
        String type = n.getType();

         try {
            if (type.equals(
                    MBeanServerNotification.REGISTRATION_NOTIFICATION)) {
                    echo("\t>> \"" +
                        ((MBeanServerNotification) n).getMBeanName() +
                        "\" has been registered in the server");
                 } else if (type.equals(
                    MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
                     echo("\t>> \"" +
                          ((MBeanServerNotification)n).getMBeanName() +
                          "\" has been unregistered from the server\n");
                 } else {
                      echo("\t>> Unknown event type (?)\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
}

In most cases, the notification object passed to the handler method is an instance of the Notification class. This class provides the notification type as well as a time-stamp, a sequence number, a message string, and user data of any type. All of these are provided by the broadcaster to pass any needed information to its listeners. Because listeners are usually registered through the MBean server, they only know the broadcaster by its object name, given by the getSource method of the notification object.


Note –

The notification model does not assume that notifications will be received in the same order that they are sent. If notification order is critical to your application, your broadcaster should set the sequence numbers appropriately, and your listeners should sort the received notifications.


The MBean server delegate sends MBeanServerNotification objects that are subclasses of the Notification class. This subclass provides two constants to identify the notification types sent by the delegate and a method that gives the object name of the MBean that was registered or unregistered. Our notification handler uses these to print out the type of operation and the object name to which the operation applies.

8.2.3 Adding a Listener Through the MBean Server

Now that we have identified the objects involved, we need to add the listener to the notification broadcaster. Our example does this in the main method of the agent application.


Example 8–2 Registering for MBean Server Delegate Notifications

AgentListener agentListener = null;
[...]

echo("\nRegistering the MBean server delegate listener...");
try {
    agentListener = new AgentListener();
    myAgent.server.addNotificationListener(
        new ObjectName(ServiceName.DELEGATE),
        agentListener, null, null);
} catch(Exception e) {
    e.printStackTrace();
    System.exit(1);
}
echo("done");

In Example 8–2, the agent application adds the AgentListener instance to the delegate MBean, which is known to be a broadcaster. The object name of the MBean server delegate is given by the DELEGATE constant in the ServiceName class. The listener is added through the addNotificationListener method of the MBean server. This method preserves the management architecture by adding listeners to MBeans while referring only to the MBean object names.

If an MBean implements the listener interface and needs to receive certain notifications, it can add itself to a broadcaster. For example, an MBean could use its preregistration method in order to add itself as a notification listener or it could expose a method that takes the object name of the notification broadcaster MBean. In both cases, its notification handler method must be designed to process all expected notification types.

The last two parameters of the addNotificationListener methods of both the MBeanServer and the NotificationBroadcaster interfaces define a filter and a handback object, respectively. Filter objects are defined by the NotificationFilter interface and provide a callback method that the broadcaster calls before calling the notification handler. If the filter is defined by the entity that adds the listener, it prevents the handler from receiving unwanted notifications.

Handback objects are added to a broadcaster along with a listener and are returned to the designated handler with every notification. The handback object is completely untouched by the broadcaster and can be used to transmit context information from the entity that adds the listener to the handler method. The functionality of filters and handback objects is beyond the scope of this tutorial; refer to the JMX specification for their full description.

8.3 Attribute Change Notifications

In this second part of the notification example, we demonstrate attribute change notifications that can be sent by MBeans. An MBean designer can choose to send notifications whenever the value of an attribute changes or is changed. The designer can implement this mechanism in any manner, according to the level of consistency required by the management solution.

The JMX specification only provides a subclass of notifications to use to represent the attribute change events: the AttributeChangeNotification class.

8.3.1 NotificationBroadcasterSupport Class

The broadcaster in our example is a very simple MBean. The reset method triggers a notification whenever it is called. This policy is specific to our example. You might want to design an MBean that sends an attribute change every time the setter is called, regardless of whether or not the value is modified. Your management needs might vary.

Example 8–3 shows the code for our SimpleStandard MBean class (the code for its MBean interface has been omitted).


Example 8–3 Broadcaster for Attribute Change Notifications

import javax.management.NotificationBroadcasterSupport;
import javax.management.MBeanNotificationInfo;
import javax.management.AttributeChangeNotification;

public class SimpleStandard
    extends NotificationBroadcasterSupport
    implements SimpleStandardMBean {

    public String getState() {
        return state;
    }

    public void setState(String s) {
        state = s;
        nbChanges++;
    }

    [...]
   
    public int getNbChanges() {
        return nbChanges;
    }

    public void reset() {
	         AttributeChangeNotification acn =
	             new AttributeChangeNotification(this,
					                                0,
					                                0,
					                                "NbChanges reset",
					                                "NbChanges",
					                                "Integer",
					                                new Integer(nbChanges),
					                                new Integer(0));
	         state = "initial state";
          nbChanges = 0;
	         nbResets++;
	         sendNotification(acn);
    }    

   public int getNbResets() {
       	return nbResets;
   }

   public MBeanNotificationInfo[] getNotificationInfo() {
        return new MBeanNotificationInfo[] {
	          new MBeanNotificationInfo(
	          new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE },
	          AttributeChangeNotification.class.getName(),
	          "This notification is emitted when the reset() method is 
            called.")
	   };
   
    private String      state = "initial state";
    private int         nbChanges = 0;
    private int         nbResets = 0;
}

This MBean sends its notifications and it implements the NotificationBroadcaster interface by extension of the NotificationBroadcasterSupport class, in order to provide all the mechanisms for adding and removing listeners and sending notifications. It manages an internal list of listeners and their handback objects and updates this list whenever listeners are added or removed. In addition, the NotificationBroadcasterSupport class provides the sendNotification method to send a notification to all listeners currently on its list.

By extending this object, our MBean inherits all of this behavior. Subclassing NotificationBroadcasterSupport is a quick and convenient way to implement notification broadcasters. We do not even have to call a superclass constructor because it has a default constructor. We only need to override the getNotificationInfo method to provide details about all of the notifications that might be sent.

8.3.2 Attribute Change Listener

Like our listener for MBean server notifications, our listener for attribute change notifications is a trivial class consisting of just the handler method.


Example 8–4 Listener for Attribute Change Notifications

import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.AttributeChangeNotification;

public class SimpleStandardListener implements NotificationListener {
    [...]

    // Implementation of the NotificationListener interface
    //
    public void handleNotification(Notification notification,
                                   Object handback) {

        // Process the different types of notifications fired by the
        // simple standard MBean.
        String type = notification.getType();

        System.out.println(
            "\n\t>> SimpleStandardListener received notification:" +
            "\n\t>> ---------------------------------------------");
        try {
            if (type.equals(AttributeChangeNotification.ATTRIBUTE_CHANGE)) {

  System.out.println("\t>> Attribute \"" +
      ((AttributeChangeNotification)notification).getAttributeName()
      + "\" has changed");
  System.out.println("\t>> Old value = " +
      ((AttributeChangeNotification)notification).getOldValue());
  System.out.println("\t>> New value = " +
      ((AttributeChangeNotification)notification).getNewValue());

            }                
            else {
                System.out.println("\t>> Unknown event type (?)\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
}

Again, we are handling a subclass of the Notification class, this one specific to attribute change notifications. The AttributeChangeNotification class provides methods for extracting the information about the attribute, notably its name, its type and its values before and after the modification. Our handler does nothing more than display these to the user. If this handler were part of an MBean in a larger management solution, it would probably take some action, depending upon the change in value of the attribute.

As demonstrated by the broadcaster's code (see Example 8–3), the subclass can easily be instantiated and sent instead of a Notification object. Its constructor provides parameters for initializing all of the attribute-related values. In our example, we do not use significant values for the sequenceNumber and timeStamp parameters because our listener has no need for them. One great advantage of Java DMK is that you only need to implement the level of functionality that you require for your management solution.


8.3.3 Adding a Listener Directly to an MBean

There is nothing that statically indicates that our MBean sends attribute change notifications. In our case it is a design decision, meaning that we know that the listener will receive attribute change notifications because we wrote the MBean that way. At runtime, the MBean server exposes the list of notifications in this MBean's metadata object, allowing a manager that is interested in attribute changes to register the appropriate listener.

Being confined to the agent, our example is much simpler. First we instantiate and register our simple MBean with the agent's MBean server. Then, because we have designed them to work together, we can add our listener for attribute changes to our MBean by calling the addNotificationListener method on the MBean server.


Example 8–5 Registering for Attribute Change Notifications

Agent myAgent = new Agent();
AgentListener agentListener = null;
SimpleStandard simpleStd = null;
ObjectName simpleStdObjectName = null;
SimpleStandardListener simpleStdListener = null;

[...]
try {
    simpleStdObjectName =
        new ObjectName("simple_mbean:class=SimpleStandard");
    simpleStd = new SimpleStandard();
    myAgent.server.registerMBean(simpleStd, simpleStdObjectName);
} catch(Exception e) {
    e.printStackTrace();
    System.exit(1);
}

echo("\nRegistering the Simple Standard MBean listener...");
try {
     simpleStdListener = new SimpleStandardListener();
     myAgent.server.addNotificationListener(simpleStdObjectName,
                                            simpleStdListener,
                                            null,
                                            null);
} catch (Exception e) {
     e.printStackTrace();
     System.exit(1);
}
echo("done");

There are two major implications when adding listeners directly rather than doing so using the MBean server:

The rest of the agent object's code performs the setup of the agent's MBean server and various input and output for running the example. Similar agents are presented in detail earlier in Part II.

8.4 Running the Agent Notification Example

Now that we have seen all of our notification broadcaster objects and all of our listener handlers, we are ready to run the example.

The examplesDir/current/Notification directory contains all of the files for the simple MBean, the listener objects, and the agent. When started, the agent application adds the MBean server delegate listener first, so that a notification can be seen for the creation of the SimpleStandard MBean. Attribute change notifications are triggered by invoking methods using the HTML adaptor.

To Run the Agent Notification Example
  1. Compile all files in this directory with the javac command.

    For example, on the Solaris platform with the Korn shell, type:


    $ cd examplesDir/current/Notification/
    $ javac -classpath classpath *.java
    
  2. To run the example, start the agent application:


    $ java -classpath classpath Agent
    
  3. After the agent application has started and added the MBean server delegate listener, press Enter to create the simple MBean.

    Before the next printout of the agent application, you should see the text generated by the AgentListener class. Its handler method has been called with an MBean creation notification, and it prints out the object name of our new MBean.

  4. Now that the simple MBean is registered and the SimpleStandardListener has been added as a listener, trigger attribute change notifications by invoking the reset operation through the HTML adaptor.

    1. Load the following URL in your browser:

      http://localhost:8082/

      If you get an error, you might have to switch off proxies in your preference settings or substitute your host name for localhost. Any browser on your local network can also connect to this agent by using your host name in this URL.

    2. In the operations table of our MBean view, click on the reset button.

      Every time you do this, you should see the output of the attribute change listener in the terminal window where you launched the agent.

  5. When you have finished with the attribute change notifications, press Enter in the agent's terminal window to remove our simple MBean.

    Again, the output of the MBean server delegate listener is displayed. This time it has detected that our MBean has been unregistered from the MBean server.

  6. Press Enter again to stop the agent application.