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?

Original source

Related problems