Timer Service (Enterprise JavaBeans 3.1)

 

Business systems frequently use scheduling systems to run programs at specified times. Scheduling systems typically 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 events such as due dates, deadlines, etc. Scheduling systems often run batch jobs (aka 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.

Regardless of the software, scheduling systems are used in many different scenarios:

• In a credit card processing system, credit card charges are processed in batches so that all the charges made for a block of time are settled together rather than separately. This work may be scheduled to be done in the evening to reduce the impact of processing on the system.

• In a hospital or clinical system, Electronic Data Interface (EDI) software is used to send medical claims to various HMOs. Each HMO has its own processing requirements, but all of them are routine, so jobs are scheduled to gather claim data, put it in the proper format, and transfer it to the HMO.

• In just about any company, managers need specific reports run on a regular basis. A scheduling system can be configured to run those reports automatically and deliver them via email to managers.

Scheduling systems are also common in workflow applications, which are 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, etc., in order to ensure everything is proceeding as scheduled. The scheduling system maintains timers and delivers events to alert applications and components when a specified date and time are reached, or when some period has expired.

Here are some examples of workflow scheduling:

• In a mortgage system, a lot of tasks have to be completed (e.g., appraisal, rate lock-in, closing appointment, etc.) before the mortgage can be closed. Timers can be set on mortgage applications to perform periodic audits that ensure everything is proceeding on schedule.

• In a healthcare claims-processing system, claims must be processed within 90 days according to terms negotiated by in-network physicians and clinics. Each claim could have a timer set to go off seven days before the deadline.

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

The EJB 2.1 specification introduced a standard Java EE scheduling system called the EJB Timer Service. The 3.1 revision of the spec has overhauled the mechanism in which users define scheduling events, bringing forth a new natural-language syntax that is much friendlier than previous versions and the cron format.

The Java Standard Edition includes the java.util.Timer class, which allows threads to schedule tasks for future execution in a background thread. This facility is 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 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 it calls that bean’s ejbTimeout() method or a method annotated with @javax.ejb.Timeout when it goes off. The rest of this topic describes the EJB Timer Service API and its use with stateless session and message-driven beans.


Example: A Batch Credit Card Processing System

Imagine we have a popular retail store with a series of 20 registers. Every minute, sales transactions take place that eventually must be processed with a third-party credit provider. Considering that we’re only one store out of potentially millions using the provider, this adds up to a lot of traffic. If our business rules allow, we might be better served caching transactions in a pending state, and then processing a batch altogether in a single request. With the EJB Timer Service, we can schedule this job to fire within our EJBs.

The Business Interface

For the credit-card-processing subsystem, we only need to support a few features. First, we must be able to accept new transactions, which will be added to the pending queue. Next, we’ll supply callers with a mechanism to schedule a batch-processing job. As a convenience, we’ll also expose support to obtain the current pending transactions and a way to immediately process them. When a scheduled processing event is fired, it’ll be able to simply call upon the business method to process.

tmp97306_thumb1_thumb1tmp97307_thumb_thumb

javax.ejb.ScheduleExpression and @javax.ejb.Schedule

The scheduleProcessing method just shown accepts a type we haven’t yet seen. New to EJB 3.1, javax.ejb.ScheduleExpression encapsulates an intuitive, flexible syntax for defining when timer events should fire. It’s here that we can programmatically indicate scheduling by year, month, day of the month, day of the week, hour, minute, and second. Additionally, we can specify a wildcard character (*) to denote “every”, a list such as 0,2,6, or ranges such as 0-10. Section 18.2.1 of the EJB 3.1 Specification defines the format in full, but some examples are shown in Table 19-1.

tmp97308_thumb_thumb

The ScheduleExpression is used to programatically create a new javax.ejb.Timer via the javax.ejb.TimerService (we’ll see both in a bit). Alternatively, it’s possible to create new Timers in a declarative fashion via the @javax.ejb.Schedule annotation:

tmp97309_thumb_thumbtmp97310_thumb_thumb

The Timer Service enables an enterprise bean to be notified when a specific date has arrived, when some period of time has elapsed, or at recurring intervals. To use the Timer Service, an enterprise bean must implement the javax.ejb.TimedObject interface, which defines a single callback method, ejbTimeout():

tmp97311_thumb_thumb

In EJB 3.x, the @javax.ejb.Timeout annotation can be applied to a method whose signature returns void and has either no parameters or one javax.ejb.Timer parameter. It’s a matter of developer preference which technique is chosen to mark a method as a timeout; here we’ll use the annotation.

When the scheduled time is reached or the specified interval has elapsed, the container system invokes the enterprise bean’s timeout callback method. The enterprise bean can then perform any processing it needs to respond to the timeout, such as run reports, audit records, modify the states of other beans, etc. In our case, we’d like to delegate along to the logic that processes pending transactions.

The Bean Implementation Class

First, we’ll make a simple singleton bean, though we could also use a stateless session bean or message-driven bean. Stateful session beans are not valid targets for the Timer Service:

tmp97312_thumb_thumb

An enterprise bean schedules itself for a timed notification using a reference to the TimerService, which can be obtained from the EJBContext or injected directly into your bean using the @javax.annotation.Resource annotation. The TimerService allows a bean to register itself for notification on a specific date, after some period of time, or at recurring intervals:

tmp97313_thumb1_thumb1

Here we accept the ScheduleExpression from the client and use it to create a new Timer from the TimerService. As the API implies, this will result in a new scheduled event to fire upon our timeout method.

The specification also makes room for automatic timer creation via @Schedule, as we saw earlier. To use this facility, simply create a timeout method and annotate it as desired:

tmp97314_thumb_thumbtmp97315_thumb_thumb

Here we’ve defined a timer that will be created by the container to fire every hour on the hour.

The TimerService

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 part of the javax.ejb package in EJB 3.x and has the following definition:

tmp97316_thumb1_thumb1

Each of the TimerService.create*Timer() methods establishes a timer with a different type of configuration. There are essentially two types of timers: single-action and interval. A single-action timer expires once, and an interval timer expires many times, at specified intervals. When a timer expires, the Timer Service calls the bean’s ejbTime out() method or a callback method with @javax.ejb.Timeout.

At this point, we are discussing only the expiration and duration parameters and their uses. The Serializable info parameter is discussed later in this topic.

When a timer is created, the Timer Service makes it persistent in some type of secondary storage, so it will survive system failures. If the server goes down, the timers are still active when the server comes back up. Although 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 vendor’s 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. The getTimers() method returns a java.util.Collection, an unordered collection of zero or more javax.ejb.Timer objects. Each Timer object represents a different timed event that has been scheduled for the bean using the Timer Service.

The getTimers() method is often used to manage existing timers. A bean can look through the Collection of Timer objects and cancel any timers that are no longer valid or need to be rescheduled.

The Timer

A Timer is an object that implements the javax.ejb.Timer interface. It 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 (optional) only parameter of the TimedObject.ejbTimeout() method or annotated @javax.ejb.Timeout callback. The Timer interface is:

tmp97317_thumb_thumbtmp97318_thumb_thumb

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.

Canceling timers

Timer.cancel() is used to cancel a specific timer from the Timer Service so that it never expires/fires. It is useful when a particular timer needs to be removed completely or simply rescheduled. To reschedule a timed event, cancel the timer and create a new one.

Identifying timers

Of course, comparing descriptions is a fairly unreliable way of identifying timers, since descriptions tend to vary over time. What is really needed is a far more robust information object that can contain both a description and a precise identifier.

All of the TimeService.createTimer() methods declare an info object as their last parameter. The info object is application data that is stored by the Timer Service and delivered to the enterprise bean when its timeout callback is invoked. The serializable object used as the info parameter can be anything, as long as it implements the java.io.Serializable interface and follows the rules of serialization.* The info object can be put to many uses, but one obvious use is to associate the timer with some sort of identifier.

To get the info object from a timer, you call the timer’s getInfo() method. This method returns a serializable object, which you’ll have to cast to an appropriate type. So far, we’ve been using strings as info objects, but there are much more elaborate (and reliable) possibilities.

Retrieving other information from timers

The Timer.getNextTimeout() method simply returns the date—represented by a java.util.Date instance—on which the timer will expire next. If the timer is a single-action timer, the Date returned is the time at which the timer will expire. If, however, the timer is an interval timer, the Date returned is the time remaining until the next expiration. Oddly, there is no way to determine subsequent expirations or the interval at which an interval timer is configured. The best way to handle this is to put that information into your info object.

The Timer.getTimeRemaining() method returns the number of milliseconds before the timer will next expire. Like the getNextTimeout() method, this method only provides information about the next expiration.

The TimerHandle object

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

tmp97319_thumb_thumb

The TimerHandle is valid only as long as the timer has not expired (if it’s a single-action timer) or been canceled. If the timer no longer exists, calling the TimerHandle.get Timer() method throws a javax.ejb.NoSuchObjectException.

TimerHandle objects are local, which means they cannot be used outside the container system that generated them. Passing the TimerHandle as an argument to a remote or endpoint interface method is illegal. However, a TimerHandle can be passed between local enterprise beans using their local interface, because local enterprise beans must be co-located in the same container system.

Exceptions

All the methods defined in the Timer interface declare two exceptions:

javax.ejb.NoSuchObjectLocalException

This exception is thrown if you invoke any method on an expired single-action timer or a canceled timer.

javax.ejb.EJBException

This exception is thrown when some type of system-level exception occurs in the Timer Service.

Transactions

When a bean calls createTimer(), the operation is performed in the scope of the current transaction. If the transaction rolls back, the timer is undone and it’s not created (or, more precisely, it’s uncreated).

In most cases, the timeout callback method on beans should have a transaction attribute of RequiresNew. This ensures that the work performed by the callback method is in the scope of container-initiated transactions.

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 work spans entities and possibly data sources. Such EJBs can also perform batch-processing work such as database cleanup, 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.

Stateless session bean timers are associated with only a specific type of session bean. When a timer for a stateless session bean goes off, the container selects an instance of that stateless bean type from the instance pool and calls its timeout callback method. This makes sense because all stateless session beans in the instance pool are logically equivalent. Any instance can serve any client, including the container itself.

Stateless session timers are often used to manage taskflow; they’re also used when the timed event applies to a collection of entities instead of just one. For example, stateless session timers might be used to audit all maintenance records to ensure that a cruise line’s ships meet state and federal guidelines. At specific intervals, a timer notifies the bean to look up the maintenance records from all the ships and generate a report. A stateless session timer can also be used to do something like send notifications to all the passengers for a particular cruise.

The stateless session bean can access an injected TimerService from the Session Context in the @PostConstruct, @PreDestroy, or any business method, but it cannot access the Timer Service from any setter injection method. This means a client must call some method on a stateless session bean (either create or a business method) in order for a timer to be set. This is the only way to guarantee that the timer is set.

Setting a timer on the @PostConstruct method within a stateless session bean is problematic. First, there is no guarantee that an @PostConstruct callback will ever be called. We might instead use a singleton configured to eagerly load on application startup to ensure the timer is registered. An SLSB @PostConstruct callback method’s stateless session bean is called sometime after the bean is instantiated, before it enters the Method-Ready Pool. However, a container might not create a pool of instances until the first client accesses that bean, so if a client (remote or otherwise) never attempts to access the bean, the @PostConstruct callback may never be called and the timer will never be set. Another problem with using SLSB @PostConstruct is that it’s called on every instance before it enters the pool; you have to prevent subsequent instances (instances created after the first instance) from setting the timer, because the first instance created would have already done this. It’s tempting to use a static variable to avoid recreating timers, as in the following code, but this can cause problems:

public class StatelessTimerBean javax.ejb.TimedObject {

tmp97320_thumb_thumb

Although this may seem like a good solution, it works only when your application is deployed within a single server with one VM and one classloader. If you are using a clustered system, a single server with multiple VMs, or multiple classloaders (which is very common), it won’t work, because bean instances that are not instantiated in the same VM with the same classloader will not have access to the same static variable. In this scenario, it’s easy to end up with multiple timers doing the same thing. An alternative is to have @PostCreate access and remove all preexisting timers to see whether the timer is already established, but this can affect performance because it’s likely that new instances will be created and added to the pool many times, resulting in many calls to @PostCreate and, therefore, many calls to TimerService.getTimers(). Also, there is no requirement that the Timer Service work across a cluster, so timers set on one node in a cluster may not be visible to timers set on some other node in the cluster.

With stateless session beans, you should never use the @PreDestroy callback method to cancel or create timers. The @PreDestroy callback is called on individual instances before they are evicted from memory. It is not called in response to client calls to the remote or local remove method. Also, the @PreDestroy callback doesn’t correspond to an un-deployment of a bean; it’s specific to only a single instance. As a result, you cannot determine anything meaningful about the EJB as a whole from a call to the ejbRemove() method, and you should not use it to create or cancel timers.

When a stateless session bean implements the javax.ejb.TimedObject interface, or contains an @javax.ejb.Timeout callback method, its lifecycle changes to include the servicing of timed events. The Timer Service pulls an instance of the bean from the instance pool when a timer expires; if there are no instances in the pool, the container creates one. Figure 19-1 shows the lifecycle of a stateless session bean that implements the TimedOut interface.

Mes

sage-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 timeout callback method.

Stateless session bean lifecycle with TimedObject

Figure 19-1. Stateless session bean lifecycle with TimedObject

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.

In order to initialize a message-driven bean timer from an incoming message, you simply put the call to the TimerService.createTimer() method in the message-handling method. For a JMS-based message-driven bean, the method call goes in the onMessage() method:

tmp97322_thumb_thumb

The incoming JMS message should contain information about the timer: the beginning (start) date, duration, or even the Serializable info object. Combining JMS with the Timer Service can offer some powerful design options for implementing audits, batch processing, and agent-like solutions.

Although it’s not standardized, it is possible that vendors will allow message-driven bean timers to be configured at deployment time. This would require a proprietary solution, since standard configuration options for message-driven bean timers do not exist. The advantage of configured timers is that they do not require a client to initiate some action to start the timer. When the bean is deployed, its timer is set automatically. This capability makes message-driven bean timers more like Unix cron jobs, which are preconfigured and then run. Consult your vendor to see whether it offers a proprietary configuration for message-driven bean timers.

As was the case for stateless session beans, the TimedObject interface or the presence of an @Timeout method changes the lifecycle of the message-driven bean slightly. When a timed event occurs, the container must pull a message-driven bean instance from the pool. If there are no instances in the pool, then an instance must be moved from the Does Not Exist state to the Method-Ready Pool before it can receive the timed event.

Next post:

Previous post: