Monday, October 30, 2006

Scheduling an Ongoing Task in a Servlet

Recently, I had the need to create a task that had to run hourly, with the stats to be available to clients. I wanted the task to be distributed without having to worry about configuring a database store, etc.


The solution I came up with was to create a JAVA servlet and use a Timer object to schedule a timer task that runs every hour. The task then runs hourly and saves the results in memory.


Usually, creating threads (which is what a TimerTask object is) in a servlet container is frowned upon because the container cannot manage the resources used by the user spawned thread. In addition, whenever your web application is deployed, reloaded, or undeployed you have to ensure that you clean up after your thread.


The first thing that you will do is implement the ServletContextListener interface. This interface consist of 2 methods that you will have to implement, namely,


public void contextInitialized (ServletContextEvent servletContextEvent);

&

public void contextDestroyed (ServletContextEvent servletContextEvent);


The contextInitialized(...) method is invoked by the servlet container everytime the servlet is 'started up'. So upon initial deployment, this method is called. Everytime you reload your web application, this method is invoked.

The contextDestroyed(...) method is invoked everytime the web application is shut down, for example, when you reload or un-deploy the web application.

Ideally, anything that you need to do when the web application first starts up should be done in the contextInitialized(...) method, while anything to undo the initialization should be done in the contextDestroyed(...) method.


In the contextInitialized(...) method, the idea is to create a TimerTask object and using the Timer object, schedule it at the desired time interval. Once the scheduling is done, we save the Timer object as an attribute in the ServletContext.

For example, assuming that MyTimerTask extends TimerTask,

  
ServletContext servletContext = servletContextEvent.getServletContext();
try{
// create the timer and timer task objects
Timer timer = new Timer();
MyTimerTask task = new MyTimerTask();

// get a calendar to initialize the start time
Calendar calendar = Calendar.getInstance();
Date startTime = calendar.getTime();

// schedule the task to run hourly
timer.scheduleAtFixedRate(task, startTime, 1000 * 60 * 60);

// save our timer for later use
servletContext.setAttribute ("timer", timer);
} catch (Exception e) {
servletContext.log ("Problem initializing the task that was to run hourly: " + e.getMessage ());
}

In the contextDestroyed(...) method, the idea is to clean up after yourself. In our case, this means cancelling any tasks and removing the timer object.


ServletContext servletContext = servletContextEvent.getServletContext();

// get our timer from the Context
Timer timer = (Timer)servletContext.getAttribute ("timer");

// cancel all pending tasks in the timers queue
if (timer != null)
timer.cancel();

// remove the timer from the servlet context
servletContext.removeAttribute ("timer");

The final thing that we have to do now is make sure that our ServletContextListener is added to our web.xml file.

As a child element of the <web-app> element, add the following lines of code:


<listener>
<listener-class>your.package.declaration.MyServletContextListener</listener-class>
</listener>

Where, your.package.declaration is the package containing the class MyServletContextListener that implements ServletContextListener!

Now everytime the web application starts up and shuts down, our listener will be invoked. Not only did we create an on-going task, but we scheduled the task and made sure that we cleaned up after ourselves too!