I just released the Starter Class of "Learn Spring Security":
1. Overview
In a previous article we demonstrated how to schedule tasks in Spring using @Scheduled annotation. In this article we will demonstrate how to achieve the same by using the timer service in a Java Enterprise Edition application for each case presented in the previous article.
2. Enable Support for Scheduling
In a Java EE application there is no need to enable support for timed tasks. The timer service is a container managed service that allows applications to call methods that are scheduled for time based events. As an example an application may have to run some daily reports at a certain hour in order to generate statistics.
There are two types of timers:
- Programmatic timers: the timer service can be injected in to any bean (except a stateful session bean) and the business logic should be placed in a method annotated with @Timeout. The timer can be initialized by a method annotated @PostConstruct of the beans or it can also be initialized just by calling a method.
- Automatic timers: the business logic is placed in any method annotated with @Schedule or @Schedules. These timers are initialized as soon as the application starts.
So let’s get started with our first example.
3. Schedule Task with a Fixed Delay
In Spring this is done simply by using the @Scheduled(fixedDelay = 1000) annotation. In this case, the duration between the end of the last execution and the start of next execution is fixed. The task always waits until the previous one is finished.
Doing exactly same thing in Java EE is a little bit harder to achieve because there is no similar built in mechanism provided, nevertheless a similar scenario can be implemented with a bit of extra coding. Let’s have a look at how this is done:
@Singleton public class FixedTimerBean { @EJB private WorkerBean workerBean; @Lock(LockType.READ) @Schedule(second = "*/5", minute = "*", hour = "*", persistent = false) public void atSchedule() throws InterruptedException { workerBean.doTimerWork(); } }
@Singleton public class WorkerBean { private AtomicBoolean busy = new AtomicBoolean(false); @Lock(LockType.READ) public void doTimerWork() throws InterruptedException { if (!busy.compareAndSet(false, true)) { return; } try { Thread.sleep(20000L); } finally { busy.set(false); } } }
As you can see the timer is scheduled to be triggered every five seconds. However the method triggered in our case simulated a 20 seconds response time by call sleep() on the current Thread.
As a consequence the container will continue to call doTimerWork() every five seconds but the condition put at the beginning of the method, busy.compareAndSet(false, true), will return immediately if the previous call has not finished. In this we ensure that the next task will be executed only after the previous one has finished.
4. Schedule Task at a Fixed Rate
One way of doing this is to use the timer service which is injected by using @Resource and configured in the method annotated @PostConstruct. The method annotated with @Timeout will be called when the timer expires.
As mentioned in the previous article the beginning of the task execution doesn’t wait for the completion of the previous execution. This option should be used when each execution of the task is independent. The following code snippet creates a timer that fires every second:
@Startup @Singleton public class ProgrammaticAtFixedRateTimerBean { @Inject Event<TimerEvent> event; @Resource TimerService timerService; @PostConstruct public void initialize() { timerService.createTimer(0,1000, "Every second timer with no delay"); } @Timeout public void programmaticTimout(Timer timer) { event.fire(new TimerEvent(timer.getInfo().toString())); } }
Another way is to use @Scheduled annotation. In the following code snippet we fire a timer every five seconds:
@Startup @Singleton public class ScheduleTimerBean { @Inject Event<TimerEvent> event; @Schedule(hour = "*", minute = "*", second = "*/5", info = "Every 5 seconds timer") public void automaticallyScheduled(Timer timer) { fireEvent(timer); } private void fireEvent(Timer timer) { event.fire(new TimerEvent(timer.getInfo().toString())); } }
5. Schedule Task with Initial Delay
If your use case scenario requires the timer to start with a delay we can do that too. In this case Java EE allows the use of the timer service. Let’s have a look at an example where the timer has an initial delay of 10 seconds and then fires every five seconds:
@Startup @Singleton public class ProgrammaticWithInitialFixedDelayTimerBean { @Inject Event<TimerEvent> event; @Resource TimerService timerService; @PostConstruct public void initialize() { timerService.createTimer(10000, 5000, "Delay 10 seconds then every 5 seconds timer"); } @Timeout public void programmaticTimout(Timer timer) { event.fire(new TimerEvent(timer.getInfo().toString())); } }
The createTimer method used in our sample is using the following signature createTimer(long initialDuration, long intervalDuration, java.io.Serializable info) where initialDuration is the number of milliseconds that must elapse before the first timer expiration notification and intervalDuration is the number of milliseconds that must elapse between timer expiration notifications.
In this example we’re using an initialDuration of 10 seconds and an intervalDuration of five seconds. The task will be executed for the first time after the initialDuration value and it will continue to be executed according to the intervalDuration.
6. Schedule Task using Cron Expressions
All the schedulers that we have seen, both programmatic and automatic, allow the use of cron expressions. Let’s see an example:
@Schedules ({ @Schedule(dayOfMonth="Last"), @Schedule(dayOfWeek="Fri", hour="23") }) public void doPeriodicCleanup() { ... }
In this example the method doPeriodicCleanup() will be called every Friday at 23:00 and on the last day of the month.
7. Conclusion
In this article we have looked at various ways to schedule tasks in the Java EE environment using as a starting point a previous article where samples were done using Spring.
Code samples can be found in the github repository.