Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run a background job method at fixed intervals? [duplicate]

I am using JSP/Servlet on Apache Tomcat. I have to run a method every 10 minutes. How can I achieve this?

like image 348
Nirbhay Mishra Avatar asked Apr 03 '13 12:04

Nirbhay Mishra


2 Answers

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.
like image 137
BalusC Avatar answered Oct 19 '22 14:10

BalusC


Read on ScheduledExecutorService it has to be initiated by a ServletContextListener

public class MyContext implements ServletContextListener 
{
    private ScheduledExecutorService sched;

    @Override
    public void contextInitialized(ServletContextEvent event) 
    {
        sched = Executors.newSingleThreadScheduledExecutor();
        sched.scheduleAtFixedRate(new MyTask(), 0, 10, TimeUnit.MINUTES);
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) 
    {
        sched.shutdownNow();
    }
}

Also, you may try using the Java Timer from a ServletContextListener but it's not recommended in a Java EE container since it takes away control of the Thread resources from the container. (the first option with ScheduledExecutorService is the way to go).

Timer timer = new Timer("MyTimer");
MyTask t = new MyTask();

//Second Parameter is the specified the Starting Time for your timer in
//MilliSeconds or Date

//Third Parameter is the specified the Period between consecutive
//calling for the method.

timer.schedule(t, 0, 1000*60*10);

And MyTask that implements TimerTask is a class that implements the Runnable interface so you have to override the run method with your code:

class MyTask extends TimerTask 
{
  public void run() 
  { 
    // your code here
  }
}
like image 45
user1697575 Avatar answered Oct 19 '22 14:10

user1697575