EJB3 Timer Service

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

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.