Timer Service API

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( ):

package javax.ejb;

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

When the scheduled time is reached or the specified interval has elapsed, the container system invokes the enterprise bean’s ejbTimeout( ) 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. For example, the Ship EJB can be modified to implement the TimedObject interface, as shown:

public abstract class ShipBean 
      implements javax.ejb.EntityBean, javax.ejb.TimedObject {
    javax.ejb.EntityContext ejbContext;
    public void setEntityContext(javax.ejb.EntityContext ctxt){
         ejbContext = ctxt;
    }
    public void ejbTimeout(javax.ejb.Timer timer) {
               // business logic for timer goes here
               }
    public Integer ejbCreate(Integer primaryKey,String name,double tonnage) {
        setId(primaryKey);
        setName(name);
        setTonnage(tonnage);
        return null;
    }    
    public void ejbPostCreate(Integer primaryKey,String name,double tonnage) {}
    public abstract void setId(Integer id);
    public abstract Integer getId( );
    public abstract void setName(String name);
    public abstract String getName( );
    public abstract void setTonnage(double tonnage);
    public abstract double getTonnage( );
    public void unsetEntityContext( ){}
    public void ejbActivate( ){}
    public void ejbPassivate( ){}
    public void ejbLoad( ){}
    public void ejbStore( ){}
    public void ejbRemove( ){}
}

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, 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 Calendar 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);

This 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 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.

We can add a method, scheduleMaintenance( ), to the Ship EJB that allows a client to schedule a maintenance item. When the method is called, the client passes in a description of the maintenance item and the date on which it is to be performed. For example, a client could schedule a maintenance item for the cruise ship Valhalla on April 2, 2004, as shown in the following code snippet:

InitialContext jndiCntxt = new InitialContext( );
ShipHomeRemote shipHome = 
  (ShipHomeRemote) jndiCntxt.lookup("java:comp/env/ejb/ShipHomeRemote");
ShipRemote ship = shipHome.findByName("Valhalla");
Calendar april2nd = Calendar.getInstance( );
april2nd.set(2004, Calendar.APRIL, 2);
String description = "Stress Test: Test Drive Shafts A & B ...";
ship.scheduleMaintenance( description, april2nd.getTime( ) );

The ShipBean implements the scheduleMaintenance( ) method and takes care of scheduling the event using the Timer Service, as shown below:

public abstract class ShipBean 
      implements javax.ejb.EntityBean, javax.ejb.TimedObject {
    javax.ejb.EntityContext ejbContext;
    public void setEntityContext(javax.ejb.EntityContext ctxt){
         ejbContext = ctxt;
    }
    public void scheduleMaintenance(String description, Date dateOfTest){
               TimerService timerService = ejbContext.getTimerService( );
               timerService.createTimer( dateOf, description);
               }
    public void ejbTimeout(javax.ejb.Timer timer) {
       // business logic for timer goes here
    }
    ...
}

As you can see, the Ship EJB is responsible for obtaining a reference to the Timer Service and scheduling its own events. When April 2, 2004, rolls around, the Timer Service calls the ejbTimeout( ) method on the Ship EJB representing the Valhalla. When the ejbTimeout( ) method is called, the Ship EJB sends a JMS message containing the description of the test to the Health and Safety department at Titan Cruises, alerting them that a stress test is required. Here’s how the implementation of ejbTimeout( ) looks:

public abstract class ShipBean 
      implements javax.ejb.EntityBean, javax.ejb.TimedObject {
    javax.ejb.EntityContext ejbContext;
    public void setEntityContext(javax.ejb.EntityContext ctxt){
         ejbContext = ctxt;
    }
    public void scheduleMaintenance(String description, Date dateOfTest){
        TimerService timerService = ejbContext.getTimerService( );
        timerService.createTimer( dateOf, description);
    }
    public void ejbTimeout(javax.ejb.Timer timer) {
               try{
               String description = (String)timer.getInfo( );

               InitialContext jndiContext = new InitialContext( );
               TopicConnectionFactory factory = (TopicConnectionFactory)
               jndiContext.lookup("java:comp/env/jms/TopicFactory");

               Topic topic = (Topic)
               jndiContext.lookup("java:comp/env/jms/MaintenanceTopic");

               TopicConnection connect = factory.createTopicConnection( );
               TopicSession session = connect.createTopicSession(true,0);
               TopicPublisher publisher = session.createPublisher(topic);
        
               TextMessage textMsg = session.createTextMessage( );
               textMsg.setText(description);
               publisher.publish(textMsg);
               connect.close( );
               }catch(Exception e){
               throw new EJBException(e);
               }
               }
        ....
}

The TimerService Interface

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 four TimerService.createTimer( ) 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, while an interval timer expires many times, at specified intervals. When a timer expires, the Timer Service calls the bean’s ejbTimeout( ) method.

Here’s how each of the four createTimer( ) methods works. At this point, we are only discussing the expiration and duration parameters and their uses. The Serializable info parameter is discussed later in this chapter.

createTimer(Date expiration, Serializable info)

Creates a single-action timer that expires once. The timer expires on the date set for the expiration parameter. Here’s how to set a timer that expires on July 4, 2004:

Calendar july4th = Calendar.getInstance( );  
july4th.set(2004, Calendar.JULY, 4);
timerService.createTimer(july4th.getTime( ), null);
createTimer(long duration, Serializable info)

Creates a single-action timer that only expires once. The timer expires after duration time (measured in milliseconds) has elapsed. Here’s how to set a timer that expires in 90 days:

long ninetyDays = 1000 * 60 * 60 * 24 * 90; //  90 days
timerService.createTimer(ninetyDays, null);
createTimer(Date initialExpiration, long intervalDuration, Serializable info)

Creates an interval timer that expires many times. The timer first expires on the date set for the initialExpiration parameter. After the first expiration, subsequent expirations occur at intervals equal to the intervalDuration parameter (in milliseconds). Here’s how to set a timer that expires on July 4, 2004 and continues to expire every three days after that date:

Calendar july4th = Calendar.getInstance( );  
july4th.set(2004, Calendar.JULY, 4);
long threeDaysInMillis = 1000 * 60 * 60 * 24 * 3; // 3 days
timerService.createTimer(july4th.getTime( ), threeDaysInMillis, null);
createTimer(long initialDuration, long intervalDuration, Serializable info)

Creates an interval timer that expires many times. The timer first expires after the period given by initialDuration has elapsed. After the first expiration, subsequent expirations occur at intervals given by the intervalDuration parameter. Both initialDuration and intervalDuration are in milliseconds. Here’s how to set a timer that expires in 10 minutes and continues to expire every hour thereafter:

long tenMinutes = 1000 * 60 * 10;  // 10 minutes
long oneHour = 1000 * 60 * 60; // 1 hour
timerService.createTimer(tenMinutes, oneHour, null);

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. 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. For example, if this method is called on the EJB representing the cruise ship Valhalla, it returns only the timers that are set for the Valhalla, not timers set for other ships. 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. For example, the Ship EJB defines the clearSchedule( ) method, which allows a client to cancel all of the timers on a specific ship. Here’s the implementation of clearSchedule( ):

public abstract class ShipBean 
      implements javax.ejb.EntityBean, javax.ejb.TimedObject {
    javax.ejb.EntityContext ejbContext;
    public void setEntityContext(javax.ejb.EntityContext ctxt){
         ejbContext = ctxt;
    }
    public void clearSchedule( ){
                  TimerService timerService = ejbContext.getTimerService( );
                  java.util.Iterator timers = timerService.getTimers( ).iterator( );
                  while( timers.hasNext( ) ){
                  javax.ejb.Timer timer = (javax.ejb.Timer) timers.next( );
                  timer.cancel( );
                  }
                  }
    public void scheduleMaintenance(String description, Date dateOfTest){
        // code for scheduling timer goes here
    }
    public void ejbTimeout(javax.ejb.Timer timer) {
       // business logic for timer goes here
    }
    ...
}

The logic here is simple. After getting a reference to the TimerService, we get an iterator that contains all of the Timers. Then we work through the iterator, cancelling each timer as we go. The Timer objects implement a cancel( ) method, which removes the timed event from the Timer Service so that it never expires.

Exceptions

The TimerService.getTimers( ) method can throw an IllegalStateException or an EJBException. All of the createTimer( ) methods declare these two exceptions, plus a third exception, the IllegalArgumentException. The reasons that the TimerService methods would throw these exceptions are:

java.lang.IllegalArgumentException

The duration and expiration parameters must have valid values. This exception is thrown if a negative number is used for one of the duration parameters or a null value is used for the expiration parameter, which is of type java.util.Date.

java.lang.IllegalStateException

This exception is thrown if the enterprise bean attempts to invoke one of the TimerService methods from a method where it’s not allowed. Each enterprise bean type (i.e., entity, stateless session, and message-driven) defines its own set of allowed operations. However, in general the TimerService methods can be invoked from anywhere except the EJBContext methods (i.e., setEntityContext( ), setSessionContext( ), and setMessageDrivenContext( )).

javax.ejb.EJBException

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

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 only parameter of the TimedObject.ejbTimeout( ) method. The Timer interface is:

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.

Cancelling timers

The previous section used the Timer.cancel( ) method. It’s used to cancel a specific timer: remove the timed event from the Timer Service so that it never expires. It is useful if a particular timer needs to be removed completely or simply rescheduled. To reschedule a timed event, cancel the timer and create a new one. For example, when one of the ship’s components fails and is replaced, that component must have its maintenance rescheduled: it doesn’t make sense to perform a yearly overhaul on an engine in June if it was replaced in May. The scheduleMaintenance( ) method can be modified so that it can add a new maintenance item or replace an existing one by canceling it and adding the new one.

 public abstract class ShipBean 
      implements javax.ejb.EntityBean, javax.ejb.TimedObject {
    javax.ejb.EntityContext ejbContext;
    public void setEntityContext(javax.ejb.EntityContext ctxt){
         ejbContext = ctxt;
    }
    public void scheduleMaintenance(String description, Date dateOfTest){
                     TimerService timerService = ejbContext.getTimerService( );
                     java.util.Iterator timers = timerService.getTimers( ).iterator( );
                     while( timers.hasNext( ) ){
                     javax.ejb.Timer timer = (javax.ejb.Timer) timers.next( );
                     String timerDesc = (String) timer.getInfo( );
                     if( description.equals( timerDesc)){
                     timer.cancel( );
                     }
                     }
                     timerService.createTimer( dateOf, description);
                     }
    public void ejbTimeout(javax.ejb.Timer timer) {
       // business logic for timer goes here
    }
    ...

The scheduleMaintenance( ) method first obtains a Collection of all timers defined for the Ship. It then compares the description of each timer to the description passed into the method. If there is a match, it means a timer for that maintenance item was already scheduled and should be canceled. After the while loop, the new Timer is added to the Timer Service.

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 with by the Timer Service and delivered to the enterprise bean when its ejbTimeout( ) method is called. The serializable object used as the info parameter can be anything, as long it implements the java.io.Serializable interface and follows the rules of serialization.[44] 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, 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. For example, rather than compare maintenance descriptions to find duplicate timers, Titan decided to use unique Maintenance Item Numbers (MINs). MINs and maintenance descriptions can be combined into a new MaintenanceItem object:

public class MaintenanceItem implements java.io.Serializable {
    private long maintenanceItemNumber;
    private String description;
    public MaintenanceItem(long min, String desc){
        maintenanceItemNumber = min;
        description = desc;
    }
    public long getMIN( ){
        return maintenanceItemNumber;
    }
    public String getDescription( ){
        return description;
    }
}

Using the MaintenanceItem type, we can modify the scheduleMaintenance( ) method to be more precise, as shown below (changes are in bold):

public abstract class ShipBean 
      implements javax.ejb.EntityBean, javax.ejb.TimedObject {
    javax.ejb.EntityContext ejbContext;
    public void setEntityContext(javax.ejb.EntityContext ctxt){
         ejbContext = ctxt;
    }
    public void scheduleMaintenance(
      MaintenanceItem maintenanceItem, Date dateOfTest){
        TimerService timerService = ejbContext.getTimerService( );
        java.util.Iterator timers = timerService.getTimers( ).iterator( );
        while( timers.hasNext( ) ){
                javax.ejb.Timer timer = (javax.ejb.Timer) timers.next( );
                MaintenanceItem timerMainItem = (MaintenanceItem) timer.getInfo( );
                     if( maintenanceItem.getMIN( ) == timerMainItem.getMIN( )){
                     timer.cancel( );
                   }
        }
        timerService.createTimer( dateOf, maintenanceItem);
    }
    public void ejbTimeout(javax.ejb.Timer timer) {
       // business logic for timer goes here
    }
    ...

The MaintenanceInfo class contains information about the maintenance work that is to be done and is sent to the maintenance system using JMS. When one of the timers expires, the Timer Service calls the ejbTimeout( ) method on the Ship EJB. When the ejbTimeout( ) method is called, the info object is obtained from the Timer object and used to determine which timer logic should be executed.

Exercise 13.1 in the Workbook shows how to deploy these examples in JBoss.

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. The TimerHandle is similar to the javax.ejb.Handle and javax.ejb.HomeHandle discussed in Chapter 5. It’s a reference that can be saved to a file or some other resource, then used later to regain access to the Timer. The TimerHandle interface is simple:

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

The TimerHandle is only valid 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.getTimer( ) 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.



[44] In the most basic cases, all an object needs to do to be serializable is implement the java.io.Serializable interface and make sure any nonserializable fields (e.g., JDBC connection handle) are marked as transient.

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

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