340 likes | 454 Views
Timer Service. Regardless of the software, scheduling systems are used in many different scenarios:
E N D
Timer Service • 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 an entire day are settled together rather than separately. This work is 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.
Timer Service • 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. • In the EJB world, there has been a general interest in scheduling systems that can work directly with enterprise beans. • However, prior to EJB 2.1, there has been no standard Java EE scheduling system. Enterprise JavaBeans 2.1 introduced a standardized but limited scheduling system called the Timer Service that hasn't been expanded at all in EJB 3.0.
Titan's Maintenance Timer • Titan Cruises has a policy of performing regular maintenance on its ships. • For example, the engines require extensive and varied maintenance activities throughout the year, as do navigation equipment, communications, sewer and water systems, etc. • In fact, there are literally thousands of maintenance functions to be performed on a ship throughout the year. • To manage all of these items, Titan uses the EJB Timer Service to alert the proper maintenance crews when an item needs to be serviced.
Timer Service API • 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) ; } • In EJB 3.0, the @javax.ejb.Timeout annotation can be applied to a method whose signature returns void and has one javax.ejb.Timer parameter. • For example, the Ship Maintenance EJB can be modified to implement the TimedObject interface, as shown:
Timer Service API @Stateless public class ShipMaintenanceBean implements ShipMaintenanceRemote implements javax.ejb.TimedObject { public void ejbTimeout(javax.ejb.Timer timer) { // business logic for timer goes here } } • Alternatively, the @javax.ejb.Timeout annotation can be used: @Stateless public class ShipMaintenanceBean implements ShipMaintenanceRemote { @Timeout public void maintenance(javax.ejb.Timer timer) { // business logic for timer goes here } }
Timer Service API • 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. TimerService timerService = // from EJBContext or injected timerService.createTimer( date, null); • For example, a client could schedule a maintenance item for the cruise ship Valhalla on April 2, 2006, as shown in the following code snippet:
Timer Service API InitialContext jndiCntxt = new InitialContext( ); ShipMaintenanceRemote maintenance = (ShipMaintenanceRemote) jndiCntxt.lookup("ShipMaintenanceRemote "); ShipMaintenanceRemote Calendar april2nd = Calendar.getInstance( ); april2nd.set(2006, Calendar.APRIL, 2); String description = "Stress Test: Test Drive Shafts A & B ..."; maintenance.scheduleMaintenance("Valhalla", description, april2nd.getTime( )); • The ShipMaintenanceBean implements the scheduleMaintenance( ) method and takes care of scheduling the event using the Timer Service, as shown here:
Timer Service API @Stateless public class ShipMaintenanceBean implements ShipMaintenanceRemote{ @Resource javax.ejb.TimerService timerService; @PersistenceContext(unitName="titanDB") entityManager; public void scheduleMaintenance(String ship, String description, Date dateOfTest) { String item = ship + " is scheduling maintenance of " + description; timerService.createTimer(dateOf, msg); } @Timeout public void maintenance(javax.ejb.Timer timer) { // business logic for timer goes here } ... }
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 part of the javax.ejb package in EJB 3.0 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;
The TimerService Interface // 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 duration. 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; }
The TimerService Interface • Here's how each of the four createTimer( ) methods works. At this point, we are discussing only 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, 2006: Calendar july4th = Calendar.getInstance( ); july4th.set(2006, Calendar.JULY, 4); timerService.createTimer(july4th.getTime( ), null);
The TimerService Interface createTimer(long duration, Serializable info) • Creates a single-action timer that expires only 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);
The TimerService Interface 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, 2006 and continues to expire every three days after that date: Calendar july4th = Calendar.getInstance( ); july4th.set(2006, Calendar.JULY, 4); long threeDaysInMillis = 1000 * 60 * 60 * 24 * 3; // 3 days timerService.createTimer(july4th.getTime( ), threeDaysInMillis, null);
The TimerService Interface 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);
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. Here are the reasons why the TimerService methods would throw these exceptions: • 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.
Exceptions 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 or annotated @javax.ejb.Timeout callback. • 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;
The Timer // 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; }
Canceling timers • The previous section used the Timer.cancel( ) method. It's used to cancel a specific timer 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.
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.
Identifying timers • For example, instead of comparing 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 shipName; private String description; public MaintenanceItem(long min, String ship, String desc) { maintenanceItemNumber = min; shipName = ship; description = desc; } public long getMIN( ) { return maintenanceItemNumber; } public String getShipName( ) { return shipName; } public String getDescription( ) { return description; } }
Identifying timers • Using the MaintenanceItem type, we can modify the scheduleMaintenance( ) method to be more precise, as shown here (changes are in bold): @Stateless public class ShipMaintenanceBean implements ShipMaintenanceRemote { @Resource javax.ejb.TimerService timerService; public void scheduleMaintenance( MaintenanceItem maintenanceItem, Date dateOfTest) { for (Object obj : timerService.getTimers( )) { MaintenanceItem scheduled = (MaintenanceItem)timer.getInfo( ); if (scheduled.getMIN() == maintenanceItem.getMIN( )) { Timer.cancel( ); } } }
Identifying timers @Timeout public void maintenance(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 maintenance( ) method on the Ship Maintenance EJB. When the maintenance( ) method is called, the info object is obtained from the Timer object and used to determine which timer logic should be executed.
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: it's not created (or, more precisely, it's uncreated). • For example, if the Ship Maintenance EJB's scheduleMaintenance( ) method has a transaction attribute of RequiresNew , a new transaction will be created when the method is called. • If an exception is thrown by the method, the transaction rolls back and the new timer event is not created. • 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 • Our 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 • 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. • Next figure shows the life cycle of a stateless session bean that implements the TimedOut interface.
Stateless Session Bean Timers Stateless session bean life cycle with TimedObject
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 timeout callback 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.
Message-Driven Bean Timers • 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: @MessageDriven public class JmsTimerBean implements MessageListener { @Resource TimerService timerService public void onMessage(Message message){ MapMessage mapMessage = (MapMessage)message; long expirationDate = mapMessage.getLong("expirationDate"); timerService.createTimer(expirationDate, null ); } @Timeout public void timeout( ){ // put timeout logic here }
Message-Driven Bean Timers • As was the case for stateless session beans, the TimedObject interface or the presence of an @Timeout method changes the life cycle 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.
Message-Driven Bean Timers Message-driven bean lifecycle with TimedObject