How to run a background job method at fixed intervals?
background-process, jakarta-ee, java, servlets, tomcat
Solution
As you're on Tomcat, which is just a barebones servletcontainer, you can't use EJB's `@Schedule` for this which is recommended by Java EE specification. Your best bet is then the `ScheduledExecutorService` from Java 1.5's `java.util.concurrent` package. You can trigger this with help of a `ServletContextListener` like follows:
@WebListener
public class BackgroundJobManager implements ServletContextListener {
private ScheduledExecutorService scheduler;
@Override
public void contextInitialized(ServletContextEvent event) {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new SomeTask(), 0, 10, TimeUnit.MINUTES);
}
@Override
public void contextDestroyed(ServletContextEvent event) {
scheduler.shutdownNow();
}
}
where the `SomeTask` class look like this:
public class SomeTask implements Runnable {
@Override
public void run() {
// Do your job here.
}
}
If you were actually using a real Java EE container with EJB support and all on em (like Glassfish, JBoss AS, TomEE, etc), then you could use a `@Singleton` EJB with a `@Schedule` method. This way the container will worry itself about pooling and destroying threads. All you need is then the following EJB:
@Singleton
public class SomeTask {
@Schedule(hour="*", minute="*/10", second="0", persistent=false)
public void run() {
// Do your job here.
}
}
Note that this way you can continue transparently using container managed transactions the usual way (`@PersistenceContext` and so on), which isn't possible with `ScheduledExecutorService` — you'd have to manually obtain the entity manager and manually start/commit/end transaction, but you would by default already not have another option on a barebones servletcontainer like Tomcat anyway.
Note that you should never use a `Timer` in a supposedly "lifetime long" running Java EE web application. It has the following major problems which makes it unsuitable for use in Java EE (quoted from Java Concurrency in Practice):
- `Timer` is sensitive to changes in the system clock, `ScheduledExecutorService` isn't.
- `Timer` has only one execution thread, so long-running task can delay other tasks. `ScheduledExecutorService` can be configured with any number of threads.
- Any runtime exceptions thrown in a `TimerTask` kill that one thread, thus making `Timer` dead, i.e. scheduled tasks will not run anymore (until you restart the server). `ScheduledThreadExecutor` not only catches runtime exceptions, but it lets you handle them if you want. Task which threw exception will be canceled, but other tasks will continue to run.
Problem
I am using JSP/Servlet on Apache Tomcat. I have to run a method every 10 minutes. How can I achieve this?