Of course Java EE Timer Service is not as powerful as Quartz, but it's provided out of the box :)
Last week I needed to implement a simple scheduler in my Java EE 5 app. I encountered a small problem - I wanted to start it automatically just after the application start up.
Here is how I did it.
1st approach: create timer inside @PostConstruct method
I tried this at first:
Of course it didn't work. Java EE container does not instantiate all SLSB when the app is started those components are started when they are needed (lazy loading).
So my problem was, where to inject it...
2nd approach: injecting worker SLSB it into another EJB component
Injecting into another EJB? Might be tempting, but again, that EJB component had to be first called in order to be instantiated and all its
But then it struck me, the reference to my SLSB can be injected into web container-managed
3rd approach: injecting worker SLSB into ServletContextListener
I was almost done, but this time I got an exception saying that it is illegal to create a timer within SLSB's
This time it was simple to fix.
4th and final solution
I added createTimer method to my
And then I simply call
I deployed the app and it worked like a charm.
It turned out not to be that simple (took me 15 minutes including deploying on application server!), but it works out of the box. I pity there is no EJB-specific component which is started when the EJB module is started. Wouldn't it be nice?
Note: Yes it would, and it's possible in Java EE 6. See this post's comments below :)
cheers,
Łukasz
I tried this at first:
@Local
public interface PeriodicWorker {
void callback(Timer timer);
}@Stateless
public class PeriodicWorkerImpl implements PeriodicWorker {
private static final Log log = LogFactory.getLog(PeriodicWorkerImpl.class);
@Resource
private TimerService timerService;
@PostConstruct
void init() {
log.debug("Timer Service injected " + (timerService != null));
timerService.createTimer(new Date(), 10000, PeriodicWorkerImpl.class);
}
@Timeout
@Override
public void callback(Timer timer) {
log.debug("Got timeout from timer " + timer.getInfo());
}
}Of course it didn't work. Java EE container does not instantiate all SLSB when the app is started those components are started when they are needed (lazy loading).
So my problem was, where to inject it...
2nd approach: injecting worker SLSB it into another EJB component
Injecting into another EJB? Might be tempting, but again, that EJB component had to be first called in order to be instantiated and all its
@Resource fields populated.But then it struck me, the reference to my SLSB can be injected into web container-managed
ServletContextListener object.3rd approach: injecting worker SLSB into ServletContextListener
ServletContextListener defines lifecycle methods which are called after the servlet context is initialised/destroyed.I was almost done, but this time I got an exception saying that it is illegal to create a timer within SLSB's
@PostConstruct method.This time it was simple to fix.
4th and final solution
I added createTimer method to my
PeriodicWorker and moved creation of timer there:@Stateless
public class PeriodicWorkerImpl implements PeriodicWorker {
private static final Log log = LogFactory.getLog(PeriodicWorkerImpl.class);
@Resource
private TimerService timerService;
@PostConstruct
void init() {
log.debug("Timer Service injected " + (timerService != null));
}
@Override
public void createTimer() {
timerService.createTimer(new Date(), 10000, PeriodicWorkerImpl.class);
}
@Timeout
@Override
public void callback(Timer timer) {
log.debug("Got timeout from timer " + timer.getInfo());
}
}And then I simply call
createTimer() method from my ServletContextListener.public class TimerInitialiserContextListener implements ServletContextListener {
private static final Log log = LogFactory.getLog(TimerInitialiserContextListener.class);
@EJB
private PeriodicWorker worker;
public void contextDestroyed(ServletContextEvent ctx) {
}
public void contextInitialized(ServletContextEvent ctx) {
worker.createTimer();
}
}I deployed the app and it worked like a charm.
It turned out not to be that simple (took me 15 minutes including deploying on application server!), but it works out of the box. I pity there is no EJB-specific component which is started when the EJB module is started. Wouldn't it be nice?
Note: Yes it would, and it's possible in Java EE 6. See this post's comments below :)
cheers,
Łukasz

4 comments:
How about using brand new EJB 3.1 Singleton beans with @Startup annotation?
Like for example here:
http://blog.rueedlinger.ch/2010/09/initialize-log4j-with-an-ejb-3-1-startup-bean/
Hi Kris,
You're right, if you use Java EE 6 you can use @Startup.
But in my case I had an existing Java EE 5 app - I should have mentioned that in my post first.
Thanks for the comment!
cheers,
Łukasz
Hi there,
I suspect that you've solved the easy bit here. The real tricky bit is managing this in a cluster. Exactly how many timers did you want to start up?
I think the EJB 3.1 Singleton resolves this, but what about JEE5?
Cheers,
Steve C
As we were working with Websphere specifically, we used the startup service it provided for initializing timers and doing other checks.
Post a Comment