99

I'm using Java and I want to keep a servlet continuously running in my application, but I'm not getting how to do it. My servlet has a method which gives counts of the user from a database on a daily basis as well as the total count of the users from the whole database. So I want to keep the servlet continuously running for that.

BalusC
  • 992,635
  • 352
  • 3,478
  • 3,452
pritsag
  • 1,143
  • 2
  • 10
  • 13
  • What do you mean, "continuously running"? – skaffman Jan 14 '11 at 12:41
  • 1
    what do you mean by continously running? It will run as long as your app server runs – fmucar Jan 14 '11 at 12:41
  • 2
    I don't understand why it has to run continuously... if someone wants the 'user count' then they call your servlet method and you give it to them? – trojanfoe Jan 14 '11 at 12:44
  • @trojanfoe Actually i want the usercount on daily basis,so for that i will have to run the servlet manually everyday so instead of doing that i want to run the servlet contineously.so i wont need to run the servlet everyday. – pritsag Jan 14 '11 at 12:59
  • @pritsag Servlets are not the way to solve this problem - if you want 'stats' collected throughout the day then use some other technology. – trojanfoe Jan 14 '11 at 13:07
  • @skaffman,@faith Actually to get the count i will have to run the servlet manually everyday as database will be updated everyday as users will be created everyday.so instead of running servlet manually everyday i want to keep the servlet running. – pritsag Jan 14 '11 at 13:07
  • 1
    @pritsag: A servlet is there to serve user requests, not to run batch jobs. – skaffman Jan 14 '11 at 13:08
  • @trojanfoe can u suggest me the other technologies ? – pritsag Jan 14 '11 at 13:16
  • @pritsag If the database is accessible from the client then a simple Java program using JDBC (running from the client itself) might be appropriate. Other than that you could try a scripting language (perl, python, etc.). If the database is not accessible from the client then you need to create a batch architecture on the server that perhaps stores its results on the database itself - you then then use a Servlet to allow clients access these stats. – trojanfoe Jan 14 '11 at 13:34

5 Answers5

219

Your problem is that you misunderstand the purpose of the servlet. It's intented to act on HTTP requests, nothing more. You want just a background task which runs once on daily basis.

EJB available? Use @Schedule

If your environment happen to support EJB (i.e. a real Java EE server such as WildFly, JBoss, TomEE, Payara, GlassFish, etc), then use @Schedule instead. Here are some examples:

@Singleton
public class BackgroundJobManager {

    @Schedule(hour="0", minute="0", second="0", persistent=false)
    public void someDailyJob() {
        // Do your job here which should run every start of day.
    }

    @Schedule(hour="*/1", minute="0", second="0", persistent=false)
    public void someHourlyJob() {
        // Do your job here which should run every hour of day.
    }

    @Schedule(hour="*", minute="*/15", second="0", persistent=false)
    public void someQuarterlyJob() {
        // Do your job here which should run every 15 minute of hour.
    }

    @Schedule(hour="*", minute="*", second="*/5", persistent=false)
    public void someFiveSecondelyJob() {
        // Do your job here which should run every 5 seconds.
    }

} 

Yes, that's really all. The container will automatically pickup and manage it.

EJB unavailable? Use ScheduledExecutorService

If your environment doesn't support EJB (i.e. you're not using not a real Java EE server, but a barebones servletcontainer such as Tomcat, Jetty, etc), then use ScheduledExecutorService. This can be initiated by a ServletContextListener. Here's a kickoff example:

@WebListener
public class BackgroundJobManager implements ServletContextListener {

    private ScheduledExecutorService scheduler;

    @Override
    public void contextInitialized(ServletContextEvent event) {
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(new SomeDailyJob(), 0, 1, TimeUnit.DAYS);
        scheduler.scheduleAtFixedRate(new SomeHourlyJob(), 0, 1, TimeUnit.HOURS);
        scheduler.scheduleAtFixedRate(new SomeQuarterlyJob(), 0, 15, TimeUnit.MINUTES);
        scheduler.scheduleAtFixedRate(new SomeFiveSecondelyJob(), 0, 5, TimeUnit.SECONDS);
    }

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

}

Where the job classes look like this:

public class SomeDailyJob implements Runnable {

    @Override
    public void run() {
        // Do your daily job here.
    }

}
public class SomeHourlyJob implements Runnable {

    @Override
    public void run() {
        // Do your hourly job here.
    }

}
public class SomeQuarterlyJob implements Runnable {

    @Override
    public void run() {
        // Do your quarterly job here.
    }

}
public class SomeFiveSecondelyJob implements Runnable {

    @Override
    public void run() {
        // Do your quarterly job here.
    }

}

Do not ever think about using java.util.Timer/java.lang.Thread in a Java EE / Servlet based environment

Last but not least, never directly use java.util.Timer and/or java.lang.Thread in Java EE. This is recipe for trouble. An elaborate explanation can be found in this JSF-related answer on the same question: Spawning threads in a JSF managed bean for scheduled tasks using a timer.

BalusC
  • 992,635
  • 352
  • 3,478
  • 3,452
  • 9
    @BalucS Thank you sir,your solution helped me and I learned about ScheduledExecutorService which was new to me as i m new to java.Thank you once again. – pritsag Jan 15 '11 at 07:36
  • @BalusC : Where should the class UpdateCounts be put in web.xml? – Ashwin Jun 13 '12 at 09:38
  • 1
    @Ashwin *web.xml* is a [Deployment Descriptor](https://en.wikipedia.org/wiki/Deployment_descriptor). The class *UpdateCount* is not related with deployment, so it doesn't have to be put in *web.xml* – informatik01 Aug 12 '13 at 00:15
  • 12
    One crucial issue with a `ScheduledExecutorService`: Be sure to **capture *all* exceptions** in your executor. If an exception escapes from your `run` method, the executor silently stops executing. This is a feature not a bug. Read the doc and study up with some googling. – Basil Bourque Oct 18 '14 at 17:50
  • Question: This task `scheduler.scheduleAtFixedRate(new SomeDailyJob(), 0, 1, TimeUnit.DAYS);` at what time will start? at 00:00 or every 24h from the moment the webApp will deploy in the server? – yaylitzis Sep 23 '16 at 11:20
  • @yaylitzis: just consult Javadoc: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html#scheduleAtFixedRate(java.lang.Runnable,%20long,%20long,%20java.util.concurrent.TimeUnit) (check 2nd argument) – BalusC Sep 23 '16 at 11:53
  • Just to be sure.. with `long initialDelay = 0` and `long period=1` this means at 00.00 every day right? – yaylitzis Sep 23 '16 at 11:57
  • @BalusC, what about `org.springframework.scheduling.concurrent.ScheduledExecutorTask`? – ACV Jul 03 '17 at 11:36
  • I've followed through the code in your answer and used it to create a `ServletContextListener`, using the same principles to wrap the only instance of a `ScheduledExecutorService` that exists in my EE app. (There are no other places in the code in which I've used `Thread`, `Runnable` or `ScheduledExecutorService`.) However, stopping/undeploying the application still results in an error message of `SEVERE: The web application [/MyApp] appears to have started a thread named [pool-4-thread-1] but has failed to stop it. This is very likely to create a memory leak.` I don't know why that happens. – Agi Hammerthief Jun 26 '18 at 09:12
  • Following on from my prior comment: Creating a `ThreadFactory` that sets the thread to be a daemon (`Thread t = Executors.defaultThreadFactory().newThread(runnable); t.setDaemon(true);`) and then using the created `ThreadFactory` as an argument to `Executors.newSingleThreadScheduledExecutor()` solves the issue for me. (The only I/O work done by the task in question is to read from a small JSON file. It uses `javax.mail` to send an email based on configuration in that file.) I'm not expecting the application to be stopped/reloaded for about a month. I don't forsee an issue with using a daemon. – Agi Hammerthief Jun 26 '18 at 12:15
  • 1
    @Agi: that will happen if `scheduler.shutdownNow()` is not correctly invoked as per the example. If this is not invoked, then the schedule thread will indeed keep running. – BalusC Jun 26 '18 at 15:12
  • @BalusC I noticed the scheduler is `private` in your `ServletContextListener`. So if we want to use it outside of the `ServletContextListener`, should we initialize another scheduler object with `Executors.newSingleThreadScheduledExecutor`? Or should we just make the variable in the `ServletContextListener` public? – theyuv Mar 09 '19 at 14:39
4

I would suggest using a library like quartz in order to run the task at regular intervals. What does the servlet really do ? It sends you a report ?

Twister
  • 739
  • 4
  • 9
  • yes ,it gives me the count of the user created per day and also the count of the total users in my database . – pritsag Jan 14 '11 at 13:09
  • 1
    huuu? Can you describe the FULL architecture of your system. I'm lost. – Twister Jan 14 '11 at 13:17
  • @Twister i m new to java and in learning phase sir and really dont konw much about the servlets. – pritsag Jan 14 '11 at 13:21
  • The problem is not about servlet. What is the application your are talking about ? (ps : it is a bad idea to delete your comments, especially comments I answered to) – Twister Jan 14 '11 at 13:22
  • @twister when user will hit the application,he will get all details like how much users are created today,how much users are craeted till now etc.and i want to run the servlet run in background contineously so that the user could get the updates.I kno this is not the proper explation.(ps:i know it was a bad idea.sorry for that .) – pritsag Jan 14 '11 at 13:26
  • @pritsag You may attach user date/time of creation to user account in your database and count it on the fly when user hit a button on the web page. This is more powerful and flexible since you can decide which interval you what to count. However, if you really want a cron-like system, e.g. every day, every hour, etc., you may use quartz to run a task on a certain interval. – gigadot Jan 14 '11 at 13:38
  • @gigadot thanks for your kind reply sir.actually i have implemented the method in servlet which is giving me the count perfectly.but the thing now i want is actually instaed of running the servlet on daily basis i want to keep it running continweously so that whenever the request for updates about the count is sent, the response will be sent to user. – pritsag Jan 14 '11 at 13:49
  • @pritsag I think I now understand what you want. You don't understand the purpose of servlet and how it works. that's not your fault. I suggest you read answer by @BalusC carefully. That's what you need. – gigadot Jan 14 '11 at 14:02
3

Implement two classes and call startTask() in main.

public void startTask()
{
    // Create a Runnable
    Runnable task = new Runnable() {
        public void run() {
            while (true) {
                runTask();
            }
        }
    };

    // Run the task in a background thread
    Thread backgroundThread = new Thread(task);
    // Terminate the running thread if the application exits
    backgroundThread.setDaemon(true);
    // Start the thread
    backgroundThread.start();
}

public void runTask()
{
    try {
        // do something...         
        Thread.sleep(1000);

    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
pfm
  • 5,440
  • 2
  • 33
  • 42
  • 3
    This is definitely NOT the way to do it in a web application - look at the answer above by @BalusC instead - he is correct here and I would say you can trust all of his answers. – Yoshiya May 21 '18 at 13:46
2

You can use cron4j. http://www.sauronsoftware.it/projects/cron4j/manual.php

Rookie
  • 8,182
  • 15
  • 55
  • 89
0

In a production system that may have multiple non-jee containers running. Use anot enterprise scheduler like Quartz scheduler which can be configured to use a database for task maamgememt.

Jeryl Cook
  • 1,118
  • 16
  • 36