Java Dynamic Management Kit 5.0 Tutorial

Chapter 9 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 Notification2 example directory located in the main examplesDir (see Directories and Classpath in the Preface).

This chapter covers the following topics:

Overview

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 (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 9–1 summarizes how listeners register with broadcasters and then receive notifications, in an agent application.

Figure 9–1 Listener Registration and Notification Propagation

Diagram showing how listeners register with broadcasters and receive notifications

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.

NotificationBroadcaster Interface

A class must implement the NotificationBroadcaster 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 broadcaster. When the broadcaster 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 broadcaster. 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 NotificationBroadcaster interface. We know from the JMX specification that it sends notifications of the following types:


Note –

Although broadcaster objects are almost always MBeans, they should not expose the methods of the NotificationBroadcaster interface. That is, the MBean interface of a standard MBean should never extend the NotificationBroadcaster interface. As we shall see in Chapter 12, Notification Forwarding, the remoteMBeanServer interface of connector clients provides the methods needed to register for and receive notifications remotely.


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 9–1 Listener for MBean Server Delegate Notifications

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

public class AgentListener implements NotificationListener {
    [...]

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

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

        System.out.println(
            "\n\t>> AgentListener handles received notification:" +
            "\n\t>> --------------------------------------------");
        try {
            if (type.equals(
                    MBeanServerNotification.REGISTRATION_NOTIFICATION)) {
                System.out.println("\t>> \"" +
                    ((MBeanServerNotification)notification).getMBeanName() +
                    "\" has been registered in the server");
            }
            else if (type.equals(
                    MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
                System.out.println("\t>> \"" +
                    ((MBeanServerNotification)notification).getMBeanName() +
                    "\" has been unregistered from the server\n");
            }
            else {
                System.out.println("\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.

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 9–2 Registering for MBean Server Delegate Notifications

AgentListener agentListener = null;
[...]

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

In Example 9–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 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.

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.

NotificationBroadcasterSupport Class

The broadcaster in our example is a very simple MBean with only one attribute. The setter method for this attribute triggers a notification whenever the value actually changes. 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. Similarly, the fact that the reset operation changes the value of the attribute but does not send a notification is specific to our example. Your management needs might vary.

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


Example 9–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 {

    /* "SimpleStandard" does not provide any specific constructors.
     * However, "SimpleStandard" is JMX compliant with regards to 
     * constructors because the default constructor SimpleStandard() 
     * provided by the Java compiler is public.
     */ 

    public String getState() {
        return state;
    }

    // The attribute setter chooses to send a notification only if
    // the value is modified
    public void setState(String s) {
        if (state.equals(s))
            return;
        AttributeChangeNotification acn = new AttributeChangeNotification(
            this, 0, 0, null, "state", "String", state, s);
        sendNotification(acn);
        state = s;
        nbChanges++;
    }

    [...]
    // The reset operation chooses not to send a notification even though
    // it changes the value of the state attribute
    public void reset() {
        state = "initial state";
        nbChanges = 0;
        nbResets++;
    }

    // Provide details about the notification type and class that is sent
    public MBeanNotificationInfo[] getNotificationInfo() {
        
        MBeanNotificationInfo[] ntfInfoArray = new MBeanNotificationInfo[1];
        
        String[] ntfTypes = new String[1];
        ntfTypes[0] = AttributeChangeNotification.ATTRIBUTE_CHANGE;
        
        ntfInfoArray[0] = new MBeanNotificationInfo( ntfTypes,
            "javax.management.AttributeChangeNotification", 
            "Attribute change notification for the 'State' attribute.");
        return ntfInfoArray;
    }

    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.

This class implements the NotificationBroadcaster interface 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.

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 9–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 to take some action, depending upon the change in value of the attribute.

As demonstrated by the broadcaster's code (see Example 9–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 the Java DMK is that you only need to implement the level of functionality that you require for your management solution.


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. Because we have kept a direct reference to the MBean instance, we can call its addNotificationListener method directly, without going through the MBean server.


Example 9–5 Registering for Attribute Change Notifications

SimpleStandard simpleStd = null;
ObjectName simpleStdObjectName = null;
SimpleStandardListener simpleStdListener = null;
[...]

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

echo("\nAdding the simple standard MBean listener...");
try {
    simpleStdListener = new SimpleStandardListener();
    simpleStd.addNotificationListener(simpleStdListener, null, null);
} catch(Exception e) {
    e.printStackTrace();
    System.exit(0);
}
echo("done");

There are two major implications to adding our listener directly to the MBean instance:

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.

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/Notification2 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 MBean. Attribute change notifications are triggered by modifying attributes through 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/Notification2/
    $ 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 modifying the State attribute through the HTML adaptor.

    1. Load the following URL in your browser:

      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 attribute table of our MBean view, type a new value for the State attribute and click the Apply 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.