March 21, 2009, 4:29 a.m.
posted by hashspark
Stateless Session Bean TimersOur Ship Maintenance EJB example was an example of a stateless session bean's usage of the Timer Service. 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 they 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, thus avoiding the timer attack problem. The stateless session bean can access an injected TimerService from the SessionContext 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 is problematic. First, there is no guarantee that an @PostConstruct callback will ever be called. An @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@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 timerthe first instance created would have already done this. It's tempting to use a static variable to avoid re-creating timers, as in the following code, but this can cause problems:
public class StatelessTimerBean javax.ejb.TimedObject {
static boolean isTimerSet = false;
@Resource TimerService timerService;
@Resource SessionContext ctx;
@PostConstruct
public void init( ){
if( isTimerSet == false) {
long expirationDate = (Long)ctx.lookup("expirationDate");
timerService.createTimer(expirationDate, null );
isTimerSet = true;
}
}
While 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 (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 if 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 undeployment 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 life cycle 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 shows the life cycle of a stateless session bean that implements the TimedOut interface. Stateless session bean life cycle with TimedObject![]() |
- Comment
