How to manage a Timer / Timertask over the web


I have implemented a Timer in java using the following code:

public static void main(String[] args) {

    Timer timer = new Timer("XMLFileReader");
    ReadXMLFile t = new ReadXMLFile();

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

Every 10 seconds an XML file should be downloaded and processed. ReadXMLFile is a subclass of TimerTask and implements the method run. It works fine when started from a shell.

The point is that I have to start / stop (i.e. run, cancel) this timer over the web. That is, I should be able to send a request to my glassfish (version 3.1.2.2 build 5) server with an action parameter like this:

http://localhost:8080/myDownloadXML?action=run

respectively

http://localhost:8080/service?action=cancel

For doing this I wrote a class which is as follows:

@Path("/service")
public class PollService {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

@GET
public Response poll(@QueryParam("action") String action) {

if (!action.equals("run") && !action.equals("cancel")) {
    String msg = "wrong action (run | cancel) " +   action + " " + action.length();
    logger.error(msg);

    return Response.status(HttpURLConnection.HTTP_BAD_REQUEST).entity(msg).build();
}

ReadXMLFile rXmlFile = new ReadXMLFile();
Timer timer = null;

if (action.equals("run")) {
    timer = new Timer("XMLFileReader");
    timer.schedule(rXmlFile, 0, delay * 1000);

    return Response.ok("Running").build();
}

if (action.equals("cancel")) {
     rXmlFile.cancel();

     return Response.ok("Cancelling").build();
}

return Response.ok().build();
}
}

I can run my timer but I cannot cancel it. As far as I can imagine the cancel method is wrong because it is being applied to an object which is not the one which is running.

How Can I stop my Timer using a GET request via http?



You are right, the timer task you are trying to stop is not the one you've started because you create a new task on every HTTP GET. You need to store the reference to your task in some sort of singleton (in this context, an object that is not created on every request). A few options:

  • I'm not sure how PollService is configured and instantiated, but if it is a sigleton in your web-app, you can just store the timer task in a class field.
  • If PollService is not a singleton you can store the reference in a static field of PollService (or some other class). In this case you need to take care of concurrent access.

I found a solution to the problem. In fact @kkamenev is right. The trick is to use the annotation @singleton and to synchronize the timer.

I am posting the "final" code:

import java.net.HttpURLConnection;
import java.util.Timer;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.jersey.spi.resource.Singleton;

@Singleton
@Path("/inrix")
public class PollService {

private static Timer timer = new Timer("XMLFileReader");
private  boolean isRunning = false;

static {
    System.out.println("INSTANTIATED!!!!!!!!!!!!!!!!!!!!!!!!");
}


@GET
public Response poll(@QueryParam("action") String action) {

    if (!action.equals("run") && !action.equals("cancel")) {
        String msg = "wrong action (run | cancel) " + action + " " + action.length();
        logger.error(msg);

        return Response.status(HttpURLConnection.HTTP_BAD_REQUEST).entity(msg).build();
    }

    ReadKMLFile rKmlFile = new ReadKMLFile();

    if (action.equals("run")) {

        synchronized (timer) {
            if (isRunning) {
                return Response.ok("Process is already running").build();
            } else {
                // Delay in seconds
                timer.schedule(rKmlFile, 0, 100 * 1000);
                isRunning = true;
                return Response.ok("started...").build();
            }
        }

    }

    if (action.equals("cancel")) {

        synchronized (timer) {
              if (!isRunning) {             
                return Response.ok("Process is not running...").build();
              } else {
                timer.cancel();
                isRunning = false;
                return Response.ok("Cancelling").build();
              }
            }

    }

    return Response.ok().build();
  }

}