Chapter 10. The Timer Service

The timer service is a special-purpose notification broadcaster designed to send notifications at specific time intervals, starting at a particular date and time. Like the other agent services we have looked at, the timer service is required for all compliant JMX implementations. In addition, the timer service is an MBean, so it can be managed (although it does not have to be registered with the MBean server to be used). There are two primary uses of the timer service:

  • To send a single notification to all listeners interested in that notification type

  • To send multiple notifications that repeat at specific intervals for a set number of times, or indefinitely

The timer service is capable of sending any number of notifications at different intervals. Each notification that is to be sent by the timer service is given a notification type, defined by the agent that instructs the timer service to send that notification. In other words, unlike the monitoring services, the timer service does not send a predefined set of notification types. Instead, the agent tells the timer service what notification types to send, as well as when to start sending the notification, how many times the notification is to repeat, and the amount of time that is to elapse between each notification (for repeating notifications only, of course).

The timer service is implemented through the use of three classes:

Timer

The class that contains the implementation of the timer service

TimerMBean

The management interface of the timer service

TimerNotification

A subclass of Notification that defines an additional field to specialize the notifications sent by the timer service

Figure 10-1 shows the relationship between these classes in UML notation.

UML diagram showing the relationships between the timer service classes

Figure 10-1. UML diagram showing the relationships between the timer service classes

In this chapter, we will look at the classes in Figure 10-1 in detail, starting with the Timer class. At the end of the chapter, we will look at two applications of the timer service:

Scheduler

A simple scheduler that kicks off the sample application after a certain date

Repeated notifications

The logging facility of the sample application, which queues the messages to be written to the log and writes them to disk each time it receives a notification

The Timer Class

The Timer class is a part of the RI and contains the implementation of the timer service. Like the monitoring service classes, the Timer class uses a separate thread of execution to perform its functions. This thread is responsible for checking the list of registered listeners, sending them notifications, and managing the parameters controlling the emission of any notifications sent. The management interface of the timer service is contained in TimerMBean and contains all of the methods necessary for manipulating the timer service.

Tip

Unless otherwise specified, whenever we talk about certain restrictions or behavior of the timer service, we are referring to a single instance of the Timer class.

The TimerMBean interface is shown in Example 10-1.

Example 10-1. The TimerMBean interface

public interface TimerMBean { 
  public Integer addNotification(String type, String message, Object userData, Date date)
    throws java.lang.IllegalArgumentException;
  public Integer addNotification(String type, String message, Object userData,
                                 Date date, long period) 
    throws java.lang.IllegalArgumentException;
  public Integer addNotification(String type, String message, Object userData, 
                                 Date date, long period, long nbOccurences) 
    throws java.lang.IllegalArgumentException;
  public boolean getSendPastNotifications(  );
  public void setSendPastNotifications(boolean value);
  public void removeNotification(Integer id)
    throws InstanceNotFoundException;
  public void removeNotifications(String type)
    throws InstanceNotFoundException;
  public void removeAllNotifications(  );
  public void start(  );
  public void stop(  );
  public int getNbNotifications(  );
  public Vector getAllNotificationIDs(  );
  public Vector getNotificationIDs(String type);
  public String getNotificationType(Integer id);
  public String getNotificationMessage(Integer id);
  public Object getNotificationUserData(Integer id);
  public Date getDate(Integer id);
  public Long getPeriod(Integer id);
  public Long getNbOccurences(Integer id);
  public boolean isActive(  );
  public boolean isEmpty(  );
}

The first method we will look at is addNotification( ) , which is used by the agent to tell the timer service about a new notification it is to send. As you can see from Example 10-1, there are three ways to do this (i.e., there are three versions of addNotification( )), each of which has the following parameters in common:

type

A user-defined String that serves as the notification type and is sent along with the notification to interested listeners. Multiple notifications with the same type may be added.

message

A String message that is sent along with the notification to interested listeners.

userData

An Object that is sent along with the notification to interested listeners.

date

A Date object that contains the date and time at which the first notification is to be sent.

These parameters are the only ones that the first version of addNotification( ) takes. A notification added using the first version of addNotification( ) will be sent only once:

try {
  Timer timer = new Timer(  );
  timer.addNotification("sample.timer.flushlog", null, null, new Date(  ));
  timer.addNotificationListener(/* listener here */, null, null);
  timer.start(  );
} catch (IllegalArgumentException e) {
  // . . .
}

Once the notification has been sent, it is removed from the list of notifications that are sent by the timer service.

The second version of addNotification( ) takes an additional parameter, period, which is used to specify the amount of time that should elapse (in milliseconds) between notifications. If zero (0) is passed as this parameter, the notification will be sent only once (this has the same effect as using the first version of this method). Otherwise, the notification will repeat indefinitely at the interval specified by period:

try {
  Timer timer = new Timer(  );
  // repeat the notification every 5 seconds. . .
  timer.addNotification("sample.timer.flushlog", null, null, new Date(  ), 5000);
  timer.addNotificationListener(/* listener here */, null, null);
  timer.start(  );
} catch (IllegalArgumentException e) {
  // . . .
}

The third version of this method takes the period parameter as well as a parameter called nbOccurences (sic), which is the number of times the notification is to repeat at the interval specified by period. If the nbOccurences parameter is 0, the notifications will repeat indefinitely (as long as the period parameter is not 0), which has the same effect as using the second version of addNotification( ).

try {
  Timer timer = new Timer(  );
  // repeat the notification 10 times, 5 seconds apart
  timer.addNotification("sample.timer.flushlog", null, null, new Date(  ), 5000, 10);
  timer.addNotificationListener(/* listener here */, null, null);
  timer.start(  );
} catch (IllegalArgumentException e) {
  // . . .
}

Once the notification has been sent nbOccurences times, the notification is removed from the list of notifications sent by the timer service.

If the date parameter is null, or if either of the period or nbOccurences parameters are less than zero, an IllegalArgumentException will be thrown.

If the date parameter represents a date that is earlier than the current date, the timer service will increment date by period until the next notification date is later than the current date. If nbOccurences is specified, each time period is added to the notification date, 1 will be subtracted from nbOccurences. For example, suppose that date is 10,351 milliseconds earlier than the current date, period is 5000 and nbOccurences is 100. The timer service will add 5,000 to date as many times as it takes for the next notification date to be later than the current date (subject to the value of nbOccurences, of course). In this example, the timer service must do so three times, at which point the next notification date will be 4,649 milliseconds later than the current date. Likewise, nbOccurences is decremented three times, such that, after updating the notification date, 97 notifications remain.

Depending on the values of date, period, and nbOccurences, the notification may never be added at all. The following scenarios will result in an IllegalArgumentException (in each of these scenarios, the specified date parameter is earlier than the current date):

  • period is zero, meaning that the notification should be sent only once. However, the notification date is earlier than the current date, so the period argument of zero is not allowed.

  • period is greater than zero, meaning that the notification will repeat, but date is so much earlier than the current date that, after adding period to date nbOccurences times, the next notification date is still earlier than the current date.

If either of these scenarios occurs, the notification is never added to the list of notifications to be sent. The notification is added only after the notification date has successfully been updated to a date later than the current date. Once it has successfully been added, though, it is possible for the timer service to send any past notifications in order for the date represented by date to catch up to the current date. This may result in a flurry of notifications being sent. However, if this is the desired behavior, you can use the setSendPastNotifications( ) method (passing true as the boolean argument) to enable this behavior. This feature of the timer service is somewhat analogous to a store-and-forward feature, guaranteeing that notifications that should have been sent (in the case of, say, a system restart) will be sent.

Tip

The send past notifications feature applies to every notification added to the timer service instance prior to calling the timer’s start( ) method. Past notifications will not be sent for any notifications added after start( ) is called, regardless of the notification start date. It is not possible to selectively send past notifications (i.e., for some notifications and not for others) for a particular instance of the timer service. If this sort of selective behavior is required, you must create more than once instance of the Timer object. Notifications that require this behavior will then use the Timer object that has this feature enabled.

Notice that the return value of addNotification( ) is an Integer object. Each notification that is added is assigned a unique ID that can be used as a token to obtain information about the notification. There are several methods that require this ID as a parameter, so make sure to save the ID when you add a notification.

try {
  Timer timer = new Timer(  );
  // repeat the notification 10 times, 5 seconds apart
  Integer ID = timer.addNotification("sample.timer.flushlog", null, null,
                                                 new Date(  ), 5000, 10);
  timer.addNotificationListener(/* listener here */, null, null);
  timer.start(  );
} catch (IllegalArgumentException e) {
  // . . .
}

Tip

Notifications that will be sent a limited number of times (e.g., when you are using the first version of addNotification( ), or using the third version and specifying nbOccurences to be greater than zero) are removed from the list of notifications sent by the timer service once that limit has been reached. Once a notification has been removed, the ID returned when the notification was added is no longer valid.

The timer service provides three methods that allow for removal of one or more notifications. The first, removeNotification( ) , removes a single notification, based on the ID that corresponds to that specific notification:

try {
  Timer timer = new Timer(  );
  // repeat the notification 10 times, 5 seconds apart
  Integer ID = timer.addNotification("sample.timer.flushlog", null, null,
                                     new Date(  ), 5000, 10);
  timer.addNotificationListener(/* listener here */, null, null);
  timer.start(  );
  // later . . .
  timer.removeNotification(ID);
} catch (IllegalArgumentException e) {
  // . . .
} catch (InstanceNotFoundException e) {
  // . . .
}

Of course, to use this version of removeNotification( ), we must store the return value from addNotification( ) somewhere so that we can pass it as a parameter. In addition, we must anticipate an InstanceNotFoundException being thrown if the ID passed in does not match any notifications in the timer service’s list. The most likely cause of this is that the notification has expired and been removed from the list.

The second version of removeNotification( ) takes a String that contains the notification type, so that all notifications of that type may be removed from the timer service’s list of notifications. Recall that the timer service supports adding the same notification type multiple times. For example, we could add the notification type sample.timer.flushlog more than once, and pass a different user-defined object each time. This allows us to handle the same notification type in different ways.

try {
  Timer timer = new Timer(  );
  // repeat the notification 10 times, 5 seconds apart
  timer.addNotification("sample.timer.flushlog", null, null, new Date(  ), 5000, 10);
  timer.addNotificationListener(/* listener here */, null, null);
  timer.start(  );
  // later . . .
  timer.removeNotification("sample.timer.flushlog");
} catch (IllegalArgumentException e) {
  // . . .
} catch (InstanceNotFoundException e) {
  // . . .
}

When using this method to do a wholesale removal of a particular notification type, you don’t need the IDs that were generated by the timer service when the individual notifications were added; the notification type is sufficient. If the notification type string specified does not match any of the notification types in the timer service’s list of notifications, an InstanceNotFoundException will be thrown.

The final way to remove notifications from the timer service is to use removeAllNotifications( ) , which, as its name implies, removes every notification in the timer service. Following a call to this method, the timer service will not send any further notifications, because they have all been removed! This method is very simple to use and throws no exceptions:

try {
  Timer timer = new Timer(  );
  // repeat the notification 10 times, 5 seconds apart
  timer.addNotification("sample.timer.flushlog", null, null, new Date(  ), 5000, 10);
  timer.addNotificationListener(/* listener here */, null, null);
  timer.start(  );
  // later . . .
  timer.removeAllNotifications(  );
} catch (IllegalArgumentException e) {
  // . . .
}

There are a number of utility methods provided by the timer service that allow you to get information about its current state, as well as information about a particular notification or group of notifications. If no notifications have been added, or the removeAllNotifications( ) method has been called, the isEmpty( ) method will return true. Calling this method allows agents to determine whether or not the timer service’s list of notifications is empty.

The second utility method, getSendPastNotifications( ) , returns a boolean value indicating whether or not the timer service will send out past notifications when the date specified for the notification to begin is earlier than the current date. If this method returns true, the send past notifications feature is enabled.

If you need to know how many notifications are in the timer service’s list of notifications, use the getNbNotifications( ) method. This method returns the number of unique notifications that will be sent, not the number of different notification types that have been added. For example, if we add the notification type sample.timer.flushlog three times (and no other notifications), getNbNotifications( ) will return 3.

If you need all of the notification IDs for a particular notification type that are contained in the timer service’s list of notifications, use getNotificationIDs( ) . This method takes the notification type string and returns a java.util.Vector object. Inside the Vector object are the IDs (which are Integer objects) that were generated by the timer service for that notification type. This method is a convenient way to obtain the IDs of a particular notification type if the individual IDs were not stored somewhere following the respective calls to addNotification( ).

The timer service also provides a method to obtain all of the notification IDs for all active notifications. This method, getAllNotificationIDs( ) , returns a Vector that contains the IDs for all of the notifications in the list.

If we have the ID for a particular notification, we can get detailed information about that notification. There are five pieces of information that can be provided about a notification (recall our discussion of the addNotification( ) method) and five methods that provide that information:

  • getNotificationMessage( ), which returns the notification message

  • getUserData( ), which returns the user-defined object

  • getDate( ), which returns the date the notifications are to begin

  • getPeriod( ), which returns the period of time between notifications

  • getNbOccurences( ), which returnsthe number of times the notification is to be sent

Whenever the timer service sends a notification to a listener, it is an instance of the TimerNotification class that is sent. TimerNotification is a subclass of Notification and adds an additional read-only attribute, NotificationID. Other than this attribute, a TimerNotification should be processed just like any other notification. TimerNotification has a single constructor, whose job is to set the NotificationID attribute and delegate the rest to Notification.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset