- EJB3 Timer service allows you to write applications which enable timed notifications.
- Timers are asynchronous and stateless hence can be used in all types of beans except stateful session bean.
- There are other scheduling packages available for enterprise applications such as;
- Flux – commercial product
- Quartz – open source product
- You can schedule a timer to occur at a specific time, after a duration of time, or at timed intervals. For example, you could set timers to go off at 8:30 PM on July 29, in 15 days, or every 1 hour.
How EJB3 timer service works?
- You have to specify a callback method in bean class, often called as timeout method, which is annotated with @Timeout. This method contains the business logic that handles the timed event.
- When a timer expires (goes off), container automatically invokes this timeout method.
Creating TimerService
To create a timer, we need to create TimerService object and use one of its createTimer() method. TimerService can be accessed in one of the following ways.
Injecting TimerService using @Resource annotation
TimerService is injected by the container into the bean component using @Resource annotation.
import javax.annotation.Resource; import javax.ejb.Stateless; import javax.ejb.TimerService; import com.theopentutorials.ejb3.business.TimerRemote; @Stateless public class TimerBean implements TimerRemote { @Resource TimerService service; }
Creating TimerService using EJB context
TimerService can be accessed using EJBContext (or more specifically SessionContext or MessageDrivenContext).
Using EJBContext
import javax.annotation.Resource; import javax.ejb.EJBContext; import javax.ejb.Stateless; import javax.ejb.TimerService; import com.theopentutorials.ejb3.business.TimerRemote; @Stateless public class TimerBean implements TimerRemote { @Resource EJBContext context; public void startTimer() { TimerService service = context.getTimerService(); } }
Using SessionContext
import javax.annotation.Resource; import javax.ejb.SessionContext; import javax.ejb.Stateless; import javax.ejb.TimerService; import com.theopentutorials.ejb3.business.TimerRemote; @Stateless public class TimerBean implements TimerRemote { @Resource SessionContext context; public void startTimer() { TimerService service = context.getTimerService(); } }
Creating Timer
After getting the TimerService instance using one of the methods discussed above, you can invoke one of the createTimer() methods of the TimerService interface.
Method | Description |
---|---|
Timer createTimer(long duration, java.io.Serializable info) |
Create a single-action timer that expires after a specified duration in milliseconds and not repeated. |
Timer createTimer(long initialDuration, long intervalDuration, java.io.Serializable info) |
Creates timer at timed intervals with initial timeout and interval duration in milliseconds. |
Timer createTimer(java.util.Date expiration, java.io.Serializable info) |
Creates a single-action timer that expires at a specific time. |
Timer createTimer(java.util.Date initialExpiration, long intervalDuration, java.io.Serializable info) |
Creates an interval timer whose first expiration occurs at a given point in time and whose subsequent expirations occur after a specified interval. |
These methods throw java.lang.IllegalArgumentException, java.lang.IllegalStateException, javax.ejb.EJBException
Examples:
1.createTimer(long, serializableInstance)
Create a single-action timer that is fired after a specified duration i.e.)1 hour (60*60*1000 milliseconds) and not repeated.
public void startTimer() { Timer timer = service.createTimer(60*60*1000, null); System.out.println("Timers set"); }
2.createTimer(long, long, serializableInstance)
This code creates timer at timed intervals with initial timeout (10000 milliseconds) and interval duration of 1 hour (60*60*1000 milliseconds).
public void startTimer() { Timer timer = service.createTimer(10000, 60*60*1000, null); System.out.println("Timers set"); }
3.createTimer(date, serializableInstance)
This code creates a single-action timer that fires at a specific time (July 29th at 8:30 PM).
public void startTimer() { SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); String s = "29/07/2012 20:30:00"; try { Date date = dateFormat.parse(s); Timer timer = service.createTimer(date, null); System.out.println("Timers set"); } catch (ParseException e) { e.printStackTrace(); } }
4.createTimer(date, long, serializableInstance)
This code creates an interval timer whose first expiration occurs at a given point in time (July 29th at 8:30 PM) and whose subsequent expirations occur after a specified interval i.e.) every 30 days (30*24*60*60*1000 milliseconds).
public void startTimer() { SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); String s = "29/07/2012 20:30:00"; try { Date date = dateFormat.parse(s); Timer timer = service.createTimer(date, 30*24*60*60*1000, null); System.out.println("Timers set"); } catch (ParseException e) { e.printStackTrace(); } }
Client or any component may invoke a method in bean which creates a timer. For example, client can invoke the above method, startTimer(), on bean to start the timer.
The Timeout Method
Rules for implementing timeout method:
- Bean class can have at most one method annotated with @Timeout.
- This method must return void.
- Take javax.ejb.Timer object as the only parameter.
- May not throw application exceptions.
@Timeout public void handleTimeout(Timer timer) { System.out.println("Handle timeour event here..."); }
Refer this link for EJB3 Timer example.
Timers are persistent
- Timers are saved when the server shut downs (or even crashes). They become active again when the server is restarted.
- If a timer expires while the server is down, the container will call the @Timeout method when the server is restarted.
In JBoss AS timers are saved at,
- JBoss AS 7: <JBOSS_HOME>/standalone/data/timer-service-data.
- JBoss AS 6/5: <JBOSS_HOME>/server/<servername>/timer-service-data.
- ‘servername’ can be default
The timer persistence is a directory and if you do not want to persist the timers, you can manually delete the folder or remove the element “data-store” from the ejb3 subsystem.
<subsystem xmlns="urn:jboss:domain:ejb3:1.2"> <timer-service thread-pool-name="default"> <data-store path="timer-service-data" relative-to="jboss.server.data.dir"/> </timer-service> </subsystem>
You can also specify at creation time that it does not have to be persisted using TimerConfig and passing it when creating timer using one of the create method.
@Override public void startTimer() { TimerConfig config = new TimerConfig(); config.setPersistent(false); Timer timer = service.createSingleActionTimer(60*60*1000, config); }
Refer this link for TimerConfig example.
Cancelling Timers
Timers can be cancelled when one of the following events occur;
- When a single-event timer expires, the EJB container calls the @Timeout method and then cancels the timer.
- When the bean invokes the cancel() method of the Timer interface, the container cancels the timer.
@Timeout public void handleTimeout(Timer timer) { System.out.println("Handle timeour event here..."); timer.cancel(); }
When a method is invoked on a cancelled timer, the container throws the javax.ejb.NoSuchObjectLocalException.
Saving Timers
- Timer object can be saved in a database or file for future reference.
- Invoke getHandle() method and store the TimerHandle object in a database. (A TimerHandle object is serializable.)
- To re-instantiate the Timer object, retrieve the handle from the database and invoke getTimer() on the handle.
@Override public void startTimer() { Timer timer = service.createTimer(10000, 60*60*1000, null); TimerHandle handle = timer.getHandle(); //save handle object in database/file i.e.) serialize Timer timer2 = handle.getTimer(); }
Getting Timer Information
Timer interface defines methods for obtaining information about timers;
public long getTimeRemaining();
Get the number of milliseconds that will elapse before the next scheduled timer expiration.
public java.util.Date getNextTimeout();
Get the point in time at which the next timer expiration is scheduled to occur.
public java.io.Serializable getInfo();
The getInfo() method returns the object that was the last parameter of the createTimer invocation.
@Override public void startTimer() { Employee employee = new Employee(); employee.setName("abcd"); //save employee in database Timer timer = service.createTimer(10000, 60 * 60 * 1000, employee); } @Timeout public void handleTimeout(Timer timer) { Employee emp = (Employee) timer.getInfo(); // process emp object }
java.util.Collection getTimers()
This method returns a collection of Timer objects.
public String checkStatus() { Timer timer = null; Collection<Timer> timers = service.getTimers(); Iterator<Timer> iterator = timers.iterator(); while (iterator.hasNext()) { timer = iterator.next(); return ("Timer will expire after " + timer.getTimeRemaining() + " milliseconds."); } return ("No timer found"); }
Transactions and Timers
- Timers can survive container restart or crash.
- Timers are transactional.
- Bean creates a timer within a transaction.
- Timer creation is rolled back, if a transaction is rolled back.
- Timer cancellation is rolled back if a bean cancels a timer within a transaction that is rolled back.
- Transaction failure inside a timeout method rolls back the actions taken by the timer.