[ Team LiB ] Previous Section Next Section

JMX Notification Model and Monitors

We have thus far described the JMX architecture, built our own custom MBeans, accessed WebLogic MBeans, and learned how to manage those MBeans in an MBean server. Now we'll add the last major component to our discussion of JMX and WebLogic Server: JMX MBean notifications.

The JMX notification model is built on the Java event model. Client applications are registered as notification listeners with MBeans that are event broadcasters through the implementation of certain interfaces. Triggered by events, the broadcasting MBean sends a notification object to all registered listeners. An additional class, the notification filter, can be used to filter events based on certain parameters.

The JMX specification restricts all registered listeners to be running within the same JVM as the MBean broadcaster. The WebLogic extensions expand on the JMX specification through the use of its RemoteNotificationListener class, which can receive event objects from outside the JVM.

The Notification Broadcaster

To illustrate, we'll create a simple example using a notification broadcaster, a notification object, and a registered notification listener. We will return to our Thermostat MBean example. Because it's the job of the MBean to send its own notifications, we'll make our Thermostat MBean a notification broadcaster.

All WebLogic MBeans are already notification broadcasters because they implement the javax.management.NotificationBroadcaster interface. However, if you want to make your own custom MBean a notification broadcaster, you have two ways to do so. The first is to implement the NotificationBroadcaster interface as WebLogic MBeans do. The other is to make it extend the NotificationBroadcasterSupport class. This class already implements the NotificationBroadcaster interface, but also includes a convenient sendNotification() method.

So, returning to our Thermostat MBean, we'll make it extend the NotificationBroadcasterSupport class. It will still implement its MBean interface and Serializable:


public class Thermostat extends NotificationBroadcasterSupport
    implements ThermostatMBean, java.io.Serializable {

The only other part the Thermostat MBean that must change is the setTemp() method. We want a notification to be generated any time the setTemp() method is called. The notification will be sent to any registered listeners (which we'll define in the next step). In our setTemp() method, we'll create a new Notification object.

There are a few constructors for creating a notification. In our example, the Notification's constructor takes five parameters: A String representing the type of the notification (we'll define ours as Thermostat.test), the notification object source (our MBean class), a number representing the notification's sequence in the MBean (-1 because we don't need to keep track of sequence), the current time stamp given in milliseconds, and finally a String representing the message (ours will include the changed temperature value). Finally, we'll call the sendNotification() method of the NotificationBroadcasterSupport class, passing it our notification:


public void setTemp(int temp) {
  this.temp = temp;
  String message = "The temp has been changed to " + temp + ".";
  Notification notification = new Notification(
    "Thermostat.test",
    this,
    -1,
    System.currentTimeMillis(),
    message);
  sendNotification(notification);
}

NOTE

The sendNotification() method of the NotificationBroadcasterSupport class will block until it receives a handback object from each registered listener indicating that all notifications were received. (Handbacks are discussed in the next section.)


That's all the modification needed to make our Thermostat MBean a notification broadcaster. The complete revised Thermostat MBean is shown in Listing 37.6.

Listing 37.6 Thermostat.java
import javax.management.NotificationBroadcasterSupport;
import javax.management.Notification;

public class Thermostat extends NotificationBroadcasterSupport
    implements ThermostatMBean, java.io.Serializable {

  // declare an integer variable for the temperature
  private int temp;

  // first constructor
  public Thermostat() {
    this.temp = 0;
  }

  // second constructor
  public Thermostat(int temp) {
    this.temp = temp;
  }

  // set the MBean's temperature value
  public void setTemp(int temp) {
    this.temp = temp;
    String message = "The temp has been changed to " + temp + ".";
    Notification notification = new Notification(
      "Thermostat.test",
      this,
      -1,
      System.currentTimeMillis(),
      message);
    sendNotification(notification);
  }

  // return the MBean's temperature value
  public int getTemp() {
    return temp;
  }

  // print the MBean's temperature value
  public void printTemp() {
    System.out.println( "The temperature is " + temp + " degrees." );
  }
}

The Notification Listener

Next we'll create the notification listener class. The listener class will register itself as a listener to the notification broadcaster. It receives two parameters in its handleNotification() method: a Notification object and an Object that represents the handback. The handback is sent back to the notification broadcaster from the listener (the broadcaster will block until its handback is returned). For our notification listener, we'll modify our ThermostatClient class.

Usually, you wouldn't have your listener in the same class that registers the MBean (for WebLogic MBeans, the MBean is usually already registered), but for the sake of simplicity our client will do both. Because the addNotificationListener() method takes a parameter that is the instance of the listener object itself (in this case, our client class), we must rearrange our ThermostatClient to have a main method that calls a constructor, and have the MBean registration and notification listener added in the body of the constructor. First, let's import the necessary packages:


import weblogic.management.MBeanHome;
import weblogic.management.Helper;
import weblogic.management.RemoteMBeanServer;
import weblogic.management.RemoteNotificationListener;
import javax.management.*;

In the Sun JMX specification, broadcasters can send notifications only to listeners in the same JVM. The WebLogic extensions, however, make it possible for listeners to receive remote notifications from outside its JVM. This is done by the weblogic.management.RemoteNotificationListener interface, which implements the NotificationListener interface:


public class ThermostatClient implements RemoteNotificationListener {

The RemoteNotificationListener is very useful for us because we're registering an MBean to run in the WebLogic Server JVM, but our listener is running in a separate JVM in its own command window.

Next we'll define our main method. The main method might or might not receive a passed-in String representing an integer to change the temp attribute. If it receives a value, it calls the constructor with it. If not, it calls the constructor with an empty String:


public static void main(String[] args) {
  try {
    // call the constructor with a value
    ThermostatClient tc = new ThermostatClient(args[0]);
  }
  catch(ArrayIndexOutOfBoundsException ae) {
    // call the constructor with an empty String
    ThermostatClient tc = new ThermostatClient("");
  }
}

Now our constructor statement that receives a String:


public ThermostatClient(String input) {

The first thing we do in the constructor is create an instance of the MBeanHome with the help of the Helper object where weblogic is the default username, weblogic is the password, t3://localhost:7001 is the server's URL, and myserver is the name of the target server in the domain:



MBeanHome mbh = Helper.getMBeanHome("weblogic", "weblogic", "t3://localhost:7001",
graphics/ccc.gif "myserver");

Next, use the MBeanHome instance to create an instance of the RemoteMBeanServer:


RemoteMBeanServer mbs = mbh.getMBeanServer();

The following two lines create an instance of the MBean and create an empty ObjectName to reference it:


Thermostat tstat = new Thermostat();
ObjectName thermostatName = null;

In a try/catch block, we now instantiate the ObjectName:


try {
  thermostatName = new ObjectName("ThermostatDomain:Name=thermostat1");

Within that try/catch block, we register the MBean with the MBean server. We do this in its own try/catch block to catch an InstanceAlreadyExistsException:


try {
  mbs.registerMBean(tstat, thermostatName);
}
catch(InstanceAlreadyExistsException ie) {
  System.out.println("MBean ("+thermostatName+") is already registered.");
}

Now we add the notification listener to the MBean. The listener may be added by either the MBean directly or by the MBean server instance through the use of the addNotificationListener() method (which is contained in both the MBeanServer interface and the NotificationBroadcasterSupport class that the MBean extends). We'll add the listener by way of the MBean server because communication with an MBean is preferably done through the MBean server.

The MBeanServer's addNotificationListener() method takes four parameters: the ObjectName of the MBean, the listener object, the notification filter object (optional), and the handback object.

A notification filter is an object that implements the NotificationFilter class and does just what it says. Notifications are sent to the filter and those meeting certain criteria are passed to the listener. For our simple discussion, we won't use any filters, so we'll leave this null. The handback object is the context that's sent to the listener when a notification is emitted. In the current example, we aren't passing the context to the listener, but in other cases, we might send an object to the listener containing some data depending on the notification.


mbs.addNotificationListener(thermostatName, this, null, null);

The next block of code is the conditional statement that looks to see whether a new temperature attribute was passed into the main method. If so, it converts the String to an Integer, creates an Attribute object, and sends it to the MBean server to change the MBean's attribute. The setAttribute() method takes two parameters: the ObjectName, and the Attribute:


if(input != "") {
  Integer i = new Integer(input);
  Attribute att = new Attribute("Temp", i);
  mbs.setAttribute(thermostatName, att);
}

We can then get the new attribute from the MBean server via the getAttribute() method:



System.out.println("Temperature = ("+thermostatName+") from MBeanServer = "+ mbs
graphics/ccc.gif.getAttribute(thermostatName, "Temp"));

If you want your code to unregister the MBean (convenient for testing purposes), you would use the following:


mbs.unregisterMBean(thermostatName);

For this example, however, we'll keep it commented out.

Close the try/catch blocks, and our constructor is complete:


  }
  catch(Exception e) {
    e.printStackTrace();
  }
}

The final piece of our notification listener is to provide an implementation for the RemoteNotificationListener's handleNotification() method. The handleNotification() method takes two parameters, which are received from the notification broadcaster: the notification and the handback object. We can then call methods on the notification:


  public void handleNotification(Notification n, Object handback) {
    System.out.println("Notification type: " + n.getType());
    System.out.println("Notification message: " + n.getMessage());
  }
}

The handleNotification() can be used for other tasks as well. Instead of just printing on the console, this method could be used to send an email through JavaMail if the temperature exceeded a certain tolerance level.

That's all there is to our notification listener class. The complete code is in Listing 37.7.

Listing 37.7 ThermostatClient.java
import weblogic.management.MBeanHome;
import weblogic.management.Helper;
import weblogic.management.RemoteMBeanServer;
import weblogic.management.RemoteNotificationListener;
import javax.management.*;

public class ThermostatClient implements RemoteNotificationListener {

  public static void main(String[] args) {
    try {
      // call the constructor with a value
      ThermostatClient tc = new ThermostatClient(args[0]);
    }
    catch(ArrayIndexOutOfBoundsException ae) {
      // call the constructor with an empty String
      ThermostatClient tc = new ThermostatClient("");
    }
  }
  public ThermostatClient(String input) {

    // create the MBeanHome with the Helper class
    MBeanHome mbh = Helper.getMBeanHome("weblogic", "weblogic", "t3://localhost:7001",
graphics/ccc.gif "myserver");

    // create the RemoteMBeanServer with the MBeanHome
    RemoteMBeanServer mbs = mbh.getMBeanServer();

    // create an instance of the Thermostat MBean
    Thermostat tstat = new Thermostat();

    // create an empty ObjectName
    ObjectName thermostatName = null;

    try {
      // instantiate the Object name
      thermostatName = new ObjectName ("ThermostatDomain:Name=thermostat1");

      try {
        // register the MBean with the RemoteMBeanServer
        mbs.registerMBean(tstat, thermostatName);
      }
      catch(InstanceAlreadyExistsException ie) {
        // alert if MBean already registered
        System.out.println("MBean ("+thermostatName+") is already registered.");
      }
      mbs.addNotificationListener(thermostatName, this, null, null);
      if(input != "") {
        Integer i = new Integer(input);
        Attribute att = new Attribute("Temp", i);
        mbs.setAttribute(thermostatName, att);
      }
      System.out.println("Temperature = ("+thermostatName+") from MBeanServer = " + mbs
graphics/ccc.gif.getAttribute(thermostatName, "Temp"));
      //mbs.unregisterMBean(thermostatName);
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }

  public void handleNotification(Notification n, Object handback) {
    System.out.println("Notification type: " + n.getType());
    System.out.println("Notification message: " + n.getMessage());
  }
}

To test the ThermostatClient as a notification listener, start your instance of WebLogic Server. In a separate command window, set your environment classpath and run the ThermostatClient application. In Figure 37.13, I've first registered the MBean and set no initial value (0). I then ran it again, passing in 67. The application changes the attribute to 67 and sends a notification to the client that has registered as a listener. The handleNotification() method then prints the notification's type and message:

Figure 37.13. ThermostatClient results as notification listener.

graphics/37fig13.gif

Although this is a simple example, the notification model adds communication to your MBeans. Notifications can alert you when thresholds are met or attributes are changed, and can trigger other actions based on those notifications. A pager can be set off if the number of connection pools reaches a certain level, or perhaps the number can be dynamically increased. A log file can be written to whenever a certain Web page or application is accessed. Together, MBeans and notifications give you much more control to your environment.

    [ Team LiB ] Previous Section Next Section