1

I have a Java application (running on WAS 8.5) which acts as a kind of server inventory for the client. The application has a servlet that triggers a long running process.

The process: fetches data from a third party DB, executes Java logic, writes records back to the application's own DB (these DB connections are pooled ones) .

The servlet is not load-on-startup and is manually triggered only once a month by a single Operations guy (on some particular date based on the client's choice each month). The servlet had been historically using Timer and TimerTask in this way:

public class SyncMissingServlet extends HttpServlet implements Servlet{

    public void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
        {               
            try{
                SyncMissing.runSync();
            }catch(Exception ex){
                logger.error(new LogMessage("ERROR: "), ex);
                this.sendReply(printWriter, "ERROR: " + ex.toString());
            }       
        }
}

public class SyncMissing
{
    public static void runSync() throws Exception 
    {
        Timer t = new Timer(true);

        SyncMissingTask task = new SyncMissingTask(); //SyncMissingTask is an instance of TimerTask
        // Start the synchronization 5 secs from now, and run it every 30 days.
        t.schedule(task, 5000, 2592000000l); //this 30 day timings never really worked out for the client, 
                                             //since the app server is restarted frequently for deployments.

    }
}

There is no use of Timer.close() or TimerTask.close() in the current code. Recently this Servlet seems to have got auto-trigerred, after a system reboot and restart of the WAS services on the system...and that's the worry.

While I couldn't explain the auto-trigger to my client, I proposed the following options:
1. drop off the use of Timer and TimerTask (the long-running process then runs on the servlet's thread itself)
2. instead of TimerTask, make it a regular Runnable and run it in a separate thread within the servlet thread.
3. make use of Java's Executor Service
4. migrate to Servlet 3.0 and turn the servlet into a Async servlet.
5. drop off the servlet altogether, and replace it with a batch job.

I understand that options 3 and 4 are really the recommended ones (or possibly option 5). But I have a feeling, that in my business scenario - Options 3 & 4 may be an overkill.

If the need is really a manual invocation of the servlet by only one user per month, are options 1 and 2 that bad? (my client wants the quickest solution and would certainly not fund option 5)

Tatha
  • 101
  • 8
  • By "single DB connect", did you mean a Singleton? Are you limited to one single connection in the JVM? If so, that raises issues. Edit your Question to clarify. – Basil Bourque Nov 30 '16 at 16:50
  • Sorry. I have updated that part now. All the DB connections are pooled (including the ones to connect to the third party DB). Also, the third party DB connects happen only from this servlet alone in the entire app...this servlet is kind of independent and the results of this long running process are not referenced from any other module of the application. – Tatha Nov 30 '16 at 16:57
  • You have not really said the exact nature of the concern. Do you want a task to run once per calendar month but strictly only once even if server restarts? If so, you need to record somewhere the last year-month the task was performed. Check that stored value before performing the task. – Basil Bourque Nov 30 '16 at 17:29
  • @ Basil Bourque: You are spot on. As a second step, I have planned for that (record the last run timestamp, to ensure it strictly runs once a month). However, I'm first trying to understand if there can be any performance worries in my case - if I run the process in the servlet thread itself (or a parallel thread within it). My scenario - only a planned single request to the servlet each month. – Tatha Nov 30 '16 at 18:38

2 Answers2

1

Well, if the servlet is supposed to be run only once in a month and there is only one client triggering it, it is fine to run it in the servlet's thread itself or create a new thread inside the servlet and let that do the task. The question of load and response times arises when you have a lot of clients making simultaneous requests, at which point you might want to use an Executor service or an async servlet.

Abhinav Upadhyay
  • 2,388
  • 19
  • 31
  • Please pardon my lack of knowledge on this matter. Say, if I do run the process on a separate thread within the servlet thread, and there is an unplanned system reboot while the app server is running and incidentally the thread's run() is getting executed at that very moment..what happens to the thread itself? (I suspect if the thread will be shutdown gracefully, like in case of an app server restart) – Tatha Nov 30 '16 at 18:51
  • If it is a proper reboot, then the app server will get chance to finish what it is doing and shutdown itself before the system goes down for reboot, so "ideally" it should be fine. However, depends on the init service, how long will it wait before it kills the app server :) – Abhinav Upadhyay Nov 30 '16 at 19:04
0

There is no need to activate a background task by invoking a servlet. Your web app has its own lifecycle. The Servlet spec provides hooks for your web app getting set-up and torn-down. Perfect place to launch and quit your background task without ever invoking a servlet by a client user.

No need to depend on a human user remembering to start the background task. Let your web app technology do the work for you.

Also, you may often hear/read "never launch threads from JSP or Servlet". This is worthy advice with regard to processing each incoming request for generating a web page. But background tasks (not directly related to a single servlet request) is a different animal; perfectly okay to have threads for background tasks as long as you handle them properly. By 'properly' I mean you explicitly end those threads appropriately, and you handle thread-safety issues. An example of a background task might be regularly polling a web service or database to refresh a cache of data.

ServletContextListener

If you want an automated task to be performed regularly within your web app, use a ServletContextListener.

That interface defines a pair of methods. One, contextInitialized, is called automatically when the web app launches, guaranteed to run before any HTTP requests are handled. The other method, contextDestroyed, runs when the web app is being torn down.

Tip: Marking your listener with a @WebListener annotation will cause your Servlet container to automatically notice it and instantiate when the web app is launched.

Beware of a nasty bug when doing development with NetBeans & Tomcat (development problem only, not a problem in deployment) where the web app does a double launch.

ScheduledExecutorService

In your custom class implementing that interface, in contextInitialized, establish a ScheduledExecutorService object to run your task repeatedly. In contextDestroyed, shutdown that executor. This is very important as the thread(s) of that executor will survive the shutdown of your web app and even the servlet container.

The ScheduledExecutorService technology supplants the Timer and TimerTask classes. These classes are especially not recommended for use in a Java Servlet environment.

You can store a reference to the executor in your listener object.

@WebListener
class MonthlyTaskRunner implements ServletContextListener {
    private ScheduledExecutorService scheduledExecutorService;

    void contextInitialized(ServletContextEvent see) {
        // initialize your ScheduledExecutorService. 
        // The ScheduledExecutorService will use one or more threads for its work outside of this thread running now.
        this.scheduledExecutorService = … ;
    }

    void contextInitialized(ServletContextEvent see) {
        // Shutdown the executor along with its thread(s).
        this.scheduledExecutorService.shutDown();
    }


}

I and others have posted on this extensively here on Stack Overflow, such as this. So search Stack Overflow. I have posted extensive examples in the context of Vaadin web apps, but the principles apply to any servlet web app. And see the Oracle Tutorial on Executors.

Where to store a reference to your ScheduledExecutorService once instantiated? You could store in a member variable on your context listener. But a more accessible place would be as an "attribute" on the servlet context. I describe this in detail along with example code and a nifty diagram in my Answer to another Question: Start & Stop a ScheduledExecutorService in Java EE environment using servlet

YearMonth

In that executor task, get the year-month of the current date for the time zone of your business context. Compare that year-month to one recorded when the task was last performed. Record that year-month somewhere, in a file, in a database, someplace.

Schedule your ScheduledExecutorService to run more often than necessary. Rather than worry about scheduling out a month, just let it run everyday. The check to compare current YearMonth with stored year-month requires nearly no execution time. KISS.

Java includes a YearMonth class.

YearMonth ymThen = YearMonth.parse( "2016-11" );  // Retrieve that string from storage.

ZoneId z = ZoneId.of( "America/Montreal" );
YearMonth ymNow = YearMonth.now( z );
if( ymNow.isAfter( ymThen ) ) {
    // … run the task
    String ymOutput = ymNow.toString();  
    // … write that `ymOutput` string someplace in storage.
 } // Else do nothing. Let the ScheduledExecutorService run again after its designated rest period.

Similar Questions

Community
  • 1
  • 1
Basil Bourque
  • 218,480
  • 72
  • 657
  • 915
  • I appreciate the use of the ServletContextListener and I can very well use ScheduledExecutorService insead of TimerTask. Also, the listener's contextDestroyed() method would be handy in stoping the executor correctly. However, auto-sechduling of the process at server startup -- this is exactly what my client DOESN'T WANT TO DO. It's a manual triggering of the process, once every calendar month (but not strictly on a particular day always). So, I suppose I shouldn't be using the listener's contextInitialized()...and I would still need a servlet...isn't it? – Tatha Dec 01 '16 at 08:07
  • I can do something like this then (in the servlet's doPost()), to have the process start after a 5 secs delay and without any future scheduling: `private ScheduledExecutorService scheduler; scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.schedule(new SomeDailyJob(), 5, TimeUnit.SECONDS);` The question then arises - where do I declare the instance of the ScheduledExecutorService (the first statement), so that it is available to both - the servlet's doPost for scheduling + the listener's contextDestroyed() for shutdown ? – Tatha Dec 01 '16 at 08:09
  • @Tatha So the process is manually triggered each month yet you use a Timer to schedule the next month? How can automatic scheduling be manually triggered? Seems to be a contradiction in terms. Your story is quite confused. – Basil Bourque Dec 01 '16 at 21:02
  • You are right...it sounds weird. Here's the story. When the client themselves coded it a year back, they did a scheduling using TimerTask within doPost(), but soon realized that their business need of running the process won't be a fixed date each month. However, they didn't care to knock-off the scheduling code themselves and had been triggering the servlet manually on whichever date they needed. This didn't pose a problem, as every month they had code deployments around the first 2 weeks and server restarts cancelled out any scheduling of the process from the last month's triggering. – Tatha Dec 02 '16 at 19:31
  • Recently, after they contracted us for their IT services, they now want to do away completely with the scheduling stuff and make it a regular servlet. Does this explain the situation? I wish I could have explained it better with shorter comments.. – Tatha Dec 02 '16 at 19:35
  • @Tatha Regarding where to hold a reference to your `ScheduledExecutorService`, store as "attribute" on the servlet context. See details in [my Answer to a similar Question](http://stackoverflow.com/a/40919115/642706). – Basil Bourque Dec 03 '16 at 07:05