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 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 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 selects an instance of that stateless bean type from the instance pool and calls its ejbTimeout( ) 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 or 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. In contrast, a timer for an entity bean helps that entity manage its own state. 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 the TimerService from the SessionContext in the ejbCreate( ), ejbRemove( ), or any business method, but it cannot access the timer service from the setSessionContext( ) 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 ejbCreate( ) method is problematic. First, there is no guarantee that ejbCreate( ) will ever be called. The ejbCreate( ) 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, ejbCreate( ) may never be called and the timer will never be set. Another problem with using ejbCreate( ) 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—the first instance created would have already done this. It’s tempting to use a static variable to avoid recreating timers (below), but it can cause problems.

public class StatelessTimerBean 
    implements javax.ejb.SessionBean, javax.ejb.TimedObject {
    
    static boolean isTimerSet = false;

    public void ejbCreate( ){
        if( isTimerSet == false) {
            TimerService timerService = ejbContext.getTimerService( );
            InitialContext jndiContext = new InitialContext( );
            Long expirationDate = (Long) 
              jndiContext.lookup("java:comp/env/expirationDate");
            timerService.createTimer(expirationDate.longValue( ), null );
            isTimerSet = true;
        }
    }

While this may seem like a good solution, it only works 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 ejbCreate( ) 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 ejbCreate( ) 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 ejbRemove( ) method to cancel or create timers. The ejbRemove( ) method 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 ejbRemove( ) method doesn’t correspond to an un-deployment of a bean; it’s only specific to 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, 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 13-2 shows the life cycle of a stateless session bean that implements the TimedOut interface.

Stateless session bean life cycle with TimedObject

Figure 13-2. Stateless session bean life cycle with TimedObject

Using a Stateless Session Timer

The InactiveCustomer EJB is a stateless session timer that periodically cleans inactive customer records from the database. It activates every 30 days and deletes records for customers who were created between 4 and 5 months ago, and who have never booked a cruise. Titan discovered that it accumulated a lot of these inactive customer records because customers would occasionally book a cruise, then cancel the reservation, and never return for more business. Once it’s deployed and activated, the InactiveCustomer EJB continues to work automatically until canceled. It’s a schedule-and-forget-it type of agent. Here’s the bean class definition for the InactiveCustomer EJB:

public class InactiveCustomerBean 
    implements javax.ejb.SessionBean, javax.ejb.TimedObject {

    final long THIRTY_DAYS  = 1000 * 60 * 60 * 24 * 30;// Thirty Days in Milliseconds
   
    SessionContext ejbContext;
    public void setSessionContext(javax.ejb.SessionContext cntx){
        ejbContext = cntx;
    }
    public void ejbCreate( ){}    

    public void schedule(Date begin){
         TimerService timerService = ejbContext.getTimerService( );
         TimerService.createTimer(begin, THIRTY_DAYS);
    }
                      public void ejbTimeout( ) throws EJBException {
    try{
        Calendar calendar = Calendar.getInstance( );
        calendar.add(Calendar.DATE, -120);
        Date date_120daysAgo = calendar.getTime( );

        calendar = Calendar.getInstance( );
        calendar.add(Calendar.DATE, -150);
        Date date_150daysAgo = calendar.getTime( );

        InitialContext jndiEnc = new InitialContext( );
        CustomerHomeLocal home = (CustomerHomeLocal)
          jndiEnc.lookup("java:comp/env/ejb/CustomerHomeLocal");
        Iterator customers = home.findCustomersWithNoReservations( ).iterator( );
        while(customers.hasNext( )){
            CustomerLocal customer = (CustomerLocal)customers.next( );
            Date dateCreated = customer.getDateCreated( )
            if( dateCreated.after(date_150daysAgo) && 
                dateCreated.before(date_120daysAgo)){
                 customer.remove( );
            }
         }
     }catch(Exception e){ // exception handle logic goes here }
    }
    public void ejbActivate( ){}
    public void ejbPassivate( ){}
    public void ejbRemove( ){}
}

The InactiveCustomer EJB has a single business method, schedule( ), that starts the timer, setting it to expire first on the given date, and every 30 days thereafter. In order for the InactiveCustomer EJB to function, a client application must call schedule( ) and pass in a start date. It’s probably best to develop a strategy similar to this one, in which the timer is scheduled only after an explicit call is made by a client, rather than attempting to design a stateless session bean timer that somehow automatically schedules itself when it’s deployed. The following code shows how an InvalidCustomer EJB is scheduled by a client:

InvalidCustomerHomeRemote invalidCustomer =  
  jndiEnc.lookup("java:comp/env/ejb/InvalidCustomerHomeRemote");
invalidCustomer.schedule( new Date( ) );

The ejbTimeout( ) method is hardcoded to fetch all the customers created between four and five months ago who never made reservations. These customers are removed from the system.

There are a number of improvements that could be made to this strategy. For example, the time window could be configured in the deployment descriptor or passed into the bean by the client. In addition, the schedule( ) method should remove any existing timers so that we don’t schedule multiple timers for the same task. These types of changes are left as an exercise for you to develop if you are interested.

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

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