How to run a background task in a servlet based web application?
Asked Answered
E

5

104

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.

Elle answered 14/1, 2011 at 12:39 Comment(9)
What do you mean, "continuously running"?Xylene
what do you mean by continously running? It will run as long as your app server runsRoscoeroscommon
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?Sanjak
@Sanjak 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.Elle
@Elle Servlets are not the way to solve this problem - if you want 'stats' collected throughout the day then use some other technology.Sanjak
@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.Elle
@pritsag: A servlet is there to serve user requests, not to run batch jobs.Xylene
@Sanjak can u suggest me the other technologies ?Elle
@Elle 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.Sanjak
S
225

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.

Sakai answered 14/1, 2011 at 13:45 Comment(11)
@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.Elle
@Sakai : Where should the class UpdateCounts be put in web.xml?Roturier
@Roturier web.xml is a Deployment Descriptor. The class UpdateCount is not related with deployment, so it doesn't have to be put in web.xmlEduct
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.Trahurn
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?Breastfeed
@yaylitzis: just consult Javadoc: docs.oracle.com/javase/7/docs/api/java/util/concurrent/… (check 2nd argument)Sakai
Just to be sure.. with long initialDelay = 0 and long period=1 this means at 00.00 every day right?Breastfeed
@BalusC, what about org.springframework.scheduling.concurrent.ScheduledExecutorTask?Faustena
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.Forney
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.Forney
@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.Sakai
C
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 ?

Cobber answered 14/1, 2011 at 13:8 Comment(8)
yes ,it gives me the count of the user created per day and also the count of the total users in my database .Elle
huuu? Can you describe the FULL architecture of your system. I'm lost.Cobber
@Cobber i m new to java and in learning phase sir and really dont konw much about the servlets.Elle
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)Cobber
@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 .)Elle
@Elle 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.Hoffmann
@Hoffmann 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.Elle
@Elle 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 @Sakai carefully. That's what you need.Hoffmann
P
2

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

Pseudohermaphrodite answered 22/12, 2011 at 8:13 Comment(0)
S
1

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();
    }
}
Stakhanovism answered 5/3, 2018 at 17:49 Comment(1)
This is definitely NOT the way to do it in a web application - look at the answer above by @Sakai instead - he is correct here and I would say you can trust all of his answers.Barela
S
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.

Strontia answered 21/2, 2016 at 15:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.