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:
Figure 10-1 shows the relationship between these classes in UML notation.
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:
A simple scheduler that kicks off the sample application after a certain date
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
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.
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:
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.
A String
message that is sent
along with the notification to interested listeners.
An Object
that is sent along
with the notification to interested listeners.
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.
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) { // . . . }
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
.