Java Development News:

EJB 2.1 The Timer Service

By Richard Monson-Haefel

01 Oct 2002 | TheServerSide.com

Column 1: What's New in EJB 2.1
Column 2: EJB 2.1 Web Services (Part 1)
Column 3: EJB 2.1 Web Services (Part 2)
Column 4: EJB 2.1 The Timer Service
Column 5: EJB 2.1 Adds New Funtionality to EJB QL
Column 6: EJB 2.1 The Enhanced Message-Driven Bean

Introduction

Richard Monson-Haefel is the author of Enterprise JavaBeans, 3rd Edition, Java Message Service and one of the world's leading experts on Enterprise Java. He is co-founder of OpenEJB, an open source EJB container used in Apple Computer's WebObjects, and an independent consultant.

Business systems frequently make use of scheduling systems, which can be configured to run other programs at specified times. In many cases, scheduling systems run applications that generate reports, reformat data, or do audit work at night. In other cases, scheduling systems provide callback APIs that can alert subsystems of temporal events such as due dates, deadlines, etc.

Scheduling systems often run "batch jobs" (a.k.a. "scheduled jobs"), which perform routine work automatically at a prescribed time. Users in the UNIX world frequently run scheduled jobs using cron, a simple but useful scheduling system that runs programs listed in a configuration file. Other job-scheduling systems include the OMG's COS Timer Event Service, which is a CORBA API for timed events, as well as commercial products.

Scheduling systems are also common in workflow applications, systems that manage document processing that typically spans days or months, and involves many systems and lots of human intervention. In workflow applications scheduling is employed for auditing tasks that periodically take inventory of the state of an application, invoice, sales order, or whatever to ensure that everything is proceeding as scheduled. The scheduling system maintains timers, and delivers events to alert applications and components when a specified date and time is reached, or when some period has expired.

In the EJB world, there has been a general interest in scheduling systems that can work directly with enterprise beans. Some products exist to support scheduling systems in J2EE, like Sims Computing's Flux® and BEA's WebLogic Time Services, but until EJB 2.1 there has not been a standard J2EE scheduling system. Enterprise JavaBeans 2.1 introduces a standardized, but limited, scheduling system of its own called the Timer Service.

The Java™ 2 Platform, Standard Edition includes the class java.util.Timer, which allows threads to schedule tasks for future execution in a background thread. This facility can be very useful for a variety of applications, but it's too limited to be used in enterprise computing. Note, however, that the scheduling semantics of java.util.Timer are very similar to those of the EJB Timer Service.

The Timer Service is a facility of the EJB container system that provides a timed-event API, which can be used to schedule timers for specified dates, periods, and intervals. A timer is associated with the enterprise bean that set it, and calls that bean's ejbTimedout() method when it goes off. The rest of this article describes the EJB Timer Service API, its use with entity, stateless session, and message-driven beans, and provides some criticism and suggested improvements of the Timer Service.


Timer Service API

The Timer Service enables an enterprise bean to be notified when a specific date has arrived, or when some period of time has elapsed, or at recurring intervals.


The TimedObject Interface

To use the Timer Service, an enterprise bean must implement the javax.ejb.TimedObject interface, which defines a single callback method, ejbTimeout():

package javax.ejb;

public interface TimedObject {
    public void ejbTimeout(Timer timer) ;
}

When the scheduled date and time is reached, or the specified period or interval has elapsed, the container system invokes the enterprise bean's ejbTimeout() method. The enterprise bean can then do any processing it needs to in response to the timeout, such as run reports, audit records, modify the states of other beans, etc.


The TimerService Interface

An enterprise bean schedules itself for a timed notification using a reference to the TimerService, which it obtains from the EJBContext. The TimerService allows a bean to register itself for notification on a specific date, or after some period of time, or at recurring intervals. The following code shows how a bean would register for notification exactly 30 days from now.


// Create a Calandar object that represents the time 30 days from now.
Calendar time = Calendar.getInstance();  // the current time.
time.add(Calendar.DATE, 30); // add 30 days to the current time.
Date date = time.getTime();

// Create a timer that will go off 30 days from now.
EJBContext ejbContext = // ...: get EJBContext object from somewhere.
TimerService timerService = ejbContext.getTimerService();
timerService.createTimer( date,  null);


The above example creates a Calendar object that represents the current time, then increments this object by 30 days so that it represents the time 30 days from now. The code then obtains a reference to the container's TimerService and calls the TimerService.createTimer() method, passing it the java.util.Date value of the Calendar object, thus creating a timer that will go off after 30 days.

The TimerService interface provides an enterprise bean with access to the EJB container's Timer Service so that new timers can be created and existing timers can be listed. The TimerService interface is a part of the javax.ejb package in EJB 2.1 and has the following definition:

package javax.ejb;
import java.util.Date;
import java.io.Serializable;
public interface TimerService {

   // Create a single-action timer that expires on a specified date.
   public Timer createTimer(Date expiration, Serializable info)
      throws IllegalArgumentException,IllegalStateException,EJBException;
   // Create a single-action timer that expires after a specified duration.
   public Timer createTimer(long duration, Serializable info)
          throws IllegalArgumentException,IllegalStateException,EJBException;
   // Create an interval timer that starts on a specified date..
   public Timer createTimer(Date initialExpiration, long intervalDuration, Serializable info)
          throws IllegalArgumentException,IllegalStateException,EJBException;
   // Create an interval timer that starts after a specified durration.
    public Timer createTimer(long initialDuration, long intervalDuration, Serializable info)
         throws IllegalArgumentException,IllegalStateException,EJBException;
  //  Get all the active timers associated with this bean
  public java.util.Collection getTimers()
         throws IllegalStateException,EJBException;

}


Each of the TimerService.createTimer() methods establishes a timer with a different temporal configuration. There are essentially two types: single-action timers and interval timers. A single-action timer expires once, while an interval timer expires many times, at specified intervals. The term "expires" means the timer "goes off" or is activated. When a timer expires, the Timer Service calls the bean's ejbTimeout() method.

When a timer is created the Timer Service automatically makes it persistent in some type of secondary storage, so it will survive system failures. If the server goes down, the timers will still be active when it comes back up again. While the specification isn't clear, it's assumed that any timers that expire while the system is down will go off when it comes back up again. If an interval timer expires many times while the server is down, it may go off multiple times when the system comes up again. Consult your vendors' documentation to learn how they handle expired timers following a system failure.

The TimerService.getTimers() method returns all the timers that have been set for a particular enterprise bean. This method returns a java.util.Collection, an unordered collection of zero or more javax.ejb.Timer objects.


The Timer Interface

A timer object is an instance of a class that implements the javax.ejb.Timer interface, and represents a timed event that has been scheduled for an enterprise bean using the Timer Service. Timer objects are returned by the TimerService.createTimer() and TimerService.getTimers() methods, and a Timer is the only parameter of the TimedObject.ejbTimeout() method. The Timer interface is defined as follows:

package javax.ejb;

public interface Timer {
    // Cause the timer and all its associated expiration notifications to be canceled
    public void cancel()
         throws IllegalStateException,NoSuchObjectLocalException,EJBException;
    // Get the information associated with the timer at the time of creation.
    public java.io.Serializable getInfo()
         throws IllegalStateException,NoSuchObjectLocalException,EJBException;
    // Get the point in time at which the next timer expiration is scheduled to occur.
    public java.util.Date getNextTimeout()
         throws IllegalStateException,NoSuchObjectLocalException,EJBException;
    // Get the number of milliseconds that will elapse before the next scheduled timer expiration
    public long getTimeRemaining() 
         throws IllegalStateException,NoSuchObjectLocalException,EJBException;
    //Get a serializable handle to the timer.
    public TimerHandle getHandle()
         throws IllegalStateException,NoSuchObjectLocalException,EJBException;
}

A Timer instance represents exactly one timed event and can be used to cancel the timer, obtain a serializable handle, obtain the application data associated with the timer, and find out when the timer's next scheduled expiration will occur.

The Timer.getInfo() method returns a serializable object; an instance of a class that implements the java.io.Serializable interface. This serializable object is called the info object, and is associated with a timer when the Timer object is created. You can use pretty much anything for the info object. It should contain application information which helps the bean identify the purpose of the timer as well as data that will help in processing.

The Timer.getHandle() method returns a TimerHandle object. The TimerHandle is similar in purpose to the javax.ejb.Handle and javax.ejb.HomeHandle. It's a reference that can be saved to a file or some other resource, then used to regain access to the Timer at a later date. The TimerHandle interface is simple:

package javax.ejb;
public interface TimerHandle extends java.io.Serializable {
    public Timer getTimer() throws NoSuchObjectLocalException, EJBException;
}

Transactions and Timers

When a bean calls TimerService.createTimer(), the operation is done in the scope of the current transaction. If the transaction rolls back, the timer is undone; it's not created.

In most beans, the ejbTimeout() method should have a transaction attribute of RequiresNew, to ensure that the work performed by the ejbTimeout() method is in the scope of container-initiated transactions.


An Example Timer Bean

In a discount stockbroker system, buy-at-limit orders can be created for a specific number of shares, but only at a specified price or lower. Such orders typically have a time limit. If the stock price falls below the specified price before the time limit, the order is carried out. If the stock price does not fall below the specified price before the time limit, the timer goes off and the order is canceled.

In a J2EE application the buy-at-limit order can be represented as an entity bean called the BuyAtLimit EJB. When the bean is created it sets a timer for the expiration date handed into the create method. If the buy-at-limit order is executed, the timer is canceled. If it's not, when the timer goes off the BuyAtLimit EJB will mark itself as canceled. (We don't delete the order, because we need the record.) The class definition for the BuyAtLimit EJB would look something like the following.


package com.xyzbrokers.order;
import javax.ejb.*;
import java.util.Collection;
import java.util.Iterator;
import java.util.Date;

public class BuyAtLimitBean implements javax.ejb.EntityBean, javax.ejb.TimedObject {
    public EntityContext ejbCntxt;
    public void setEntityContext(EntityContext cntxt) {ejbCntxt = cntxt;}

    public void ejbCreate(CustomerLocal cust, String stockSymbol, int numberOfShares, double priceCap,
                          Date expiration){
               setNumberOfShares(numberOfShares);
               setStockSymbol(stockSymbol);
               setPriceCap(priceCap);
               setExpiration(expiration);
    }
    public void ejbPostCreate(CustomerLocal cust, String stockSymbol, int numberOfShares, double priceCap,
                              Date expiration){
               setCustomer(cust);
               TimerService timerService = ejbCntxt.getTimerService();
               timerService.createTimer( expiration, null );
    }
    public void ejbTimeout(Timer timer){
                cancelOrder();
    }
    public void cancelOrder(){
                setCanceled(true);
                setDateCanceled( new Date());
                cancelTimer();
    }
    public void executeOrder(){
                setExecuted(true);
                setDateExecuted( new Date());
                cancelTimer();
    }
    private void cancelTimer(){
                TimerService timerService = ejbCntxt.getTimerService();
                Iterator timers = timerService.getTimers().iterator();
                if(timers.hasNext() ){
                       Timer timer = (Timer) timers.next();
                       timer.cancel();
                }
    }
    // EJB callback methods, persistent fields, and relationships fields not shown
}


When the ejbTimeout() method is called by the container, it simply calls the bean's own cancelOrder() method, which in turn sets the persistent fields to indicate that the order is canceled and then calls a private cancelTimer() method. The cancelTimer() method obtains a reference to the bean's current timer and cancels it. This method is called when the buy order is executed or canceled by the timer, or when a client calls the cancelOrder() method directly.


Entity Bean Timers

Entity beans set timers on a specific type of entity bean (e.g., Ship, Customer, Reservation, etc.) with a specific primary key. When a timer goes off, the first thing that the container does is use the primary key associated with the timer to load the entity bean with proper data. Once the entity bean is in the ready state -- its data is loaded and it's ready to service requests -- the ejbTimeout() method is invoked. The container associates the primary key with the timer implicitly.

Using timers with entity beans can be very useful, because it allows entity beans to manage their own timed events. This makes sense, especially when the timed events are critical to the entity's definition. For example, paying claims on time is intrinsic to the definition of a claim. This is also true of the time limit on a buy-at-limit order, a payment-past-due alert on mortgages, etc.


Stateless Session Bean Timers

Stateless session bean timers can be used for auditing or batch processing. As an auditing agent, a stateless session timer can monitor the state of the system to ensure that tasks are being completed and that data is consistent. This type of audit work spans entities and possibly data sources. Such EJBs can also perform batch processing work such as database clean up, transfer of records, etc. Stateless session bean timers can also be deployed as agents that perform some type of intelligent work on behalf of the organization they serve. An agent can be thought of as an extension of an audit: it monitors the system but it also fixes problems automatically.

While entity timers are associated with a specific type of entity bean and primary key, stateless session bean timers are associated only with a specific type of session bean. When a timer for a stateless session bean goes off, the container automatically selects an arbitrary instance of that stateless bean type from the instance pool and calls its ejbTimeout() method.


Message-Driven Bean Timers

Message-driven bean timers are similar to stateless session bean timers in several ways: Timers are associated only with the type of bean. When a timer expires, a message-driven bean instance is selected from a pool to execute the ejbTimeout() method. In addition, message-driven beans can be used for performing audits or other types of batch jobs.

The primary difference between a message-driven bean timer and a stateless session bean timer is the way in which they're initiated: timers are created in response to an incoming message or, if the container supports it, from a configuration file.

If you've read this column before than you may remember that in the first installment I criticized the specification for not including message-driven beans as one of bean types that can implement the timer interface. That changed a few weeks after that article was published so that now message-driven beans can be timers. There are still problems with message-driven bean timers, though; specifically, they can not be configured at deployment time. This problem is addressed in the next section.


Problems with the Timer Service

The Timer Service is an excellent addition to the EJB platform, but it's too limited. A lot can be learned from cron, the Unix scheduling utility that's been around for years.


A very little bit about cron

Cron is a Unix program that allows you to schedule scripts (similar to batch files in DOS) , commands, and other programs to run at specified dates and times. Unlike the EJB Timer Service, cron allows for very flexible calendar-based scheduling. Cron jobs (anything cron runs is called a job) can be scheduled to run at intervals of a specific minute of the hour, hour of the day, day of the week, day of the month, and month of the year.

For example, you can schedule a cron job to run every Friday at 12:15 p.m., or every hour, or the first day of every month. While this level of refinement may sound complicated, its actually very simple to specify. Cron uses a simple text format of five fields of integer values, separated by spaces or tabs, to describe the intervals at which scripts should be run. Figure 1 shows the field positions and their meanings.



Figure 1 Cron Date and Time Format


The order of the fields is significant. since each specifies a different calendar designator: minute, hour, day, month, and day of the week.

The following examples show how to schedule cron jobs:

20 * * * * ---> 20 minutes after every hour. (00:20, 01:20, etc.)
5 22 * * * ---> Every day at 10:05 p.m.
0  8 1 * * ---> First day of every month at 8:00 a.m.
0 8 4 7 * ---> The fourth of July at 8:00 a.m.
15 12 * * 5 ---> Every Friday at 12:15 p.m.

An asterisk indicates that all values are valid. For example, if you use an asterisk for the minute field, you're scheduling cron to execute the job every minute of the hour. The following examples illustrate the impact of the asterisk on schedules:

* 10 * * * ---> Every day at 10:00, 10:01, ...10:59
0 10 * 7 * ---> Every day in July at 10:00 a.m.
* * * * * ---> Every minute of every hour of every day.

You can define more complex intervals by specifying multiple values, separated by commas, for a single field. In addition you can specify ranges of time using the hyphen:

0 8 * * 1,3,5 ---> Every Monday, Wednesday,
      and Friday at 8:00 a.m.
0 8 1,15 * * ---> The first and 15th of every month
      at 8:00 a.m.
0 8-17 * * 1-5 ---> Every hour from 8 a.m. through 5 p.m.,
      Monday through Friday

Cron jobs are scheduled using crontab files, which are simply text files in which you configure the date/time fields and a command, usually a command to run a script.


Improving The Timer Service

The cron date/time format offers a lot more flexibility than is currently offered by the EJB Timer Service. The Timer Service requires you to designate intervals in exact milliseconds, which is a bit awkward to work with (you have to convert days, hours, and minutes to milliseconds), but more important is that it's not flexible enough for many real-world scheduling needs. For example, there is no way to schedule a timer to expire on the first and 15th of every month, or every hour between 8 a.m. and 5 p.m., Monday through Friday. You can derive some of the more complex intervals, but only at the cost of adding logic to your bean code to calculate them, and in more complicated scenarios you'll need multiple timers for the same task.

Cron is not perfect either. Scheduling jobs is like setting a timer on a VCR: everything is scheduled according to the clock and calendar. You can specify that cron run a job at specific times of the day on specific days of the year, but you can't have it run a job at relative intervals from an arbitrary starting point. For example, cron's date/time format doesn't let you schedule a job to run every 10 minutes, starting now. You have to schedule it to run at specific minutes of the hour (e.g., 0,10,20,30,40,50).

Cron's is limited to scheduling recurring jobs; you can't set up a single-action timer, and you can't set a start date. A problem with both cron and the EJB Timer Service is that you can't program a stop date -- a date that the timer will automatically cancel itself.

You also may have noticed that cron granularity is to the minute rather than the millisecond. At first glance this looks like a weakness, but in practice it's perfectly acceptable. For calendar-driven scheduling, more precision simply isn't very useful.

A solution is to change the Timer Service interface so that it can handle a cron-like date/time format, with a start date and end date. Rather than discard the current createTimer() calls (which are useful, especially for single-action timers and arbitrary millisecond intervals), it would be preferable simply to add a new method with the desired cron-like semantics. In addition, instead of using 0 - 6 to designate the day of the week, it would be better to follow the lead set by the Linux version of cron, which uses the values Sun, Mon, Tue, Wed, Thu, Fri, and Sat. For example, code to schedule a timer that would run every weekday at 11:00 p.m. starting October 1, 2003, and ending May 31, 2004, would look like this:


TimerService timerService = ejbContext.getTimerService();
Calendar start = Calendar.getInstance().set(2003, Calendar.OCTOBER, 1);
Calendar end = Calendar.getInstance().set(2004, Calendar.MAY, 31);

String dateTimeString = "23   *   *   *   Mon-Fri";
timerService.createTimer(dateTimeString, start, end, null);

This proposed change to the Timer Service explicitly retains the other millisecond-based createTimer() methods, because they are very useful. While cron-like configuration is powerful, it's not a silver bullet. If you need to schedule a timer to go off every 30 seconds starting now (or any arbitrary point in time), you need to use one of the existing createTimer() methods. As I pointed out earlier, the cron-like configuration is bound to specific calendar times; it's like programming your VCR. The other millisecond-based createTimer() methods, by contrast, are more like an egg timer with millisecond precision. At any time you can set them to go off after a fixed number of milliseconds.

It should be noted, however, that true millisecond accuracy is difficult because (a) normal processing and thread contention tend to delay response time, and (b) a server clock must be properly synchronized with the correct time (i.e. UTC1 ), to the millisecond, and most are not.

In the long run, any changes to the Timer Service will need to be hammered out by the EJB expert group based on vendor and developer feedback, but the solution proposed in this article illustrates some of the possibilities of using a cron-like date/time format.

Scheduling systems can have much richer semantics than proposed above. For example, Sims Computing's Flux® supports capabilities that just can't be duplicated with a cron-like date/time format or the present EJB Timer Service. For example, scheduling jobs that skip public holidays, or that quit running after N intervals, or that run for one week and then pause for two weeks, or that run every business hour except during lunch. A Timer Service this flexible is worth shooting for, but may never be realized in a vendor-agnostic standard like Enterprise JavaBeans.


Message-Driven Bean Timers: Standard configuration properties

There is enormous potential for using message-driven beans as cron-like jobs which are configured at deployment and run automatically. Unfortunately, there is no standard way to configure a message-driven bean timer at deployment time. Some vendors may support this while others do not. Pre-configured message-driven bean timers are going to be in high demand by developers who want to schedule message-driven beans to perform work at specific dates and times. Without support for deployment-time configuration, the only reliable way to program an enterprise bean timer is to have a client call a method or send a JMS message. This is unacceptable. Developers will need deployment-time configuration and it should be added to the next version of the specification.

Building on the proposed cron-like semantics described in the previous subsection, it would be easy to devise standard activation configuration properties for configuring message-driven bean timers at deployment time. For example the following configures a message-driven bean, the Audit EJB, to run at 11 p.m., Monday through Friday, starting October 1, 2003, and ending May 31, 2004 (start and end dates are not required).


<activation-config>
    <description>Run Monday through Friday at 11:00 p.m. Starting on Oct 1st,2003 until May 31st, 2004</description>
    <activation-config-property>
        <activation-config-property-name>dateTimeFields</activation-config-property-name>
        <activation-config-property-value> 23   *   *   *   Mon-Fri</activation-config-property-value>
    </activation-config-property>
    <activation-config-property>
        <activation-config-property-name>startDate</activation-config-property-name>
        <activation-config-property-value>October 1, 2003</activation-config-property-value>
    </activation-config-property>
    <activation-config-property>
        <activation-config-property-name>endDate</activation-config-property-name>
        <activation-config-property-value>May 31, 2004</activation-config-property-value>
    </activation-config-property>
</activation-config>

This configuration would be fairly easy for providers to implement if they supported enhanced cron-like semantics, as outlined in the previous subsection. In addition, you could configure message-driven beans to use the millisecond-based timers EJB 2.1 already supports.


Other Problems with Timer API

The semantics of the Timer object convey very little information about the timer object itself. There is no way to determine whether a timer is a single-action timer or an interval timer. If it's an interval timer, there is no way to determine the configured interval, or whether the timer has executed its first expiration or not. To solve these problems, additional methods should be added to the Timer interface that provide this information. As a stopgap, it's a good idea to place this information in the info object, so that it can be accessed by applications that need it.


Wrapping Up

The primary purpose of this article was to explain how the EJB 2.1 Timer Service works and to propose some changes that I think are needed in order to make the Timer Service more useful in the real world.

Whether or not the changes outlined in this article are adopted is a matter for the EJB expert group, which should be responsive to the EJB developer community. It's likely that others will find ways to improve these proposed changes. Regardless of the outcome, the current limited semantics of the Timer Service, and the complete lack of support for configurable message-driven bean timers is a problem. As you develop timers, you will quickly discover the need for a much richer way of describing expirations, and the desire for some way to configure timers at deployment time, rather than having to use a client application to initiate a scheduled event.

Next month's article will cover changes to EJB QL, which include, among others, the new ORDER BY clause.


Notes

  1. Coordinated Universal Time (UTC) is the international standard reference time. Servers can be coordinated with UTC using the Network Time Protocol (NTP) and public time servers. Coordinated Universal Time is abbreviated UTC as a compromise among standardizing nations. A full explanation is provided by the National Institute of Standards and Technology's FAQ on UTC.



copyright notice (c) Copyright 2002 Richard Monson-Haefel. All rights reserved.


PRINTER FRIENDLY VERSION