May 4, 2010

How to Implement a Task Scheduler/ Job Scheduler in Spring Framework using Annotations?

I had a chance to analyze how the task scheduler works in Spring framework today!! The recent addition of the @Scheduled and @Async into Spring framework 3.0 forced me to tgive them a try! Emphatically, spring framework is remarkable. With a few tweaks in code, everything gets in place as expected. In this article, let me give you a very brief overview on how to implement a scheduled task in spring using @Schedule annotation! This would be a nice tutorial for a newbie and an intermediate Spring programmer! If you intend to have a scheduled job run periodically and if you are using Spring, please read further and get to know the nuances involved.Let us dive into action right away!.
  1. First and foremost, let us decide on incorporating the task:annotation-driven in the applicationContext.xml file. This is the configuration file that would be loaded by the ContextLoaderListener within your spring application. I have decided to have the task:annotation-driven tag within the application since this is where I would be including the beans that would form a part of my service layer and I am going to include the TaskScheduler class a service in my application.
    Next, go ahead and add the xmlns details to your configuration. The following code needs to be added to the applicationContext.xml file in your application.

    xmlns:task=http://www.springframework.org/schema/task
  2. Further, you have to add the schema location details for the task-annotation driven tag in your applicationContextxml. This is as follows:

    http://www.springframework.org/schema/task  <br />http://www.springframework.org/schema/task/spring-task-3.0.xsd<br />
  3. Note: If you do not add the configurations given in point # 1 and #2 in your applicationContext.xml where we would be defining the services used in our application (I am conidering that I would have a class containing the scheduled jobs as a service and this would be a part of my services package), you are likely to get the "The prefix 'task' for element '' is not bound" error.
  4. Next, you have to make your Spring container understand that you would be having some scheduled jobs within your application and those tasks are driven by annotations. You can do so by adding the following line to the applicationContext.xml file.

    <task:annotation-driven><br />
  5. Now, it is important that the Spring framework should be aware of the class details where you would be using your @Scheduled annotation. For this, let us consider we have a package or.webapp.services.tasks. In this package, let us consider that we have a class MyScheduler.java. This is the class that would contain all the methods which would be invoked as scheduled tasks/jobs in your application. It is important that you add this package in the component-scan tag within your applicationContext.xml as follows:

    <context:component-scan base-package="org.webapp.services.tasks"<br />
  6. Next, we need to code the details of the scheduled job within the class MyScheduler. As a sample, we will have one method testTask. This task would be annotated as @Scheduled with the required attributes. This would in turn make Spring container understand that the method underneath this annotation would be run as a job that is scheduled by the Spring Framework. This can be done as follows:

    package org.test.common.utils.Tasks;<br />import java.util.Date;<br />import org.springframework.scheduling.annotation.Scheduled;<br />import org.springframework.stereotype.Service;<br />@Service<br />public class MyScheduler {<br />  <br />    @Scheduled(fixedRate = 5000)<br />    public void process() {<br />        System.out.println("Invoking testTask at " + new Date());<br />    }<br /><br />}<br />
  7. Attributes for @Scheduled Annotation:If you want a job or a task to be run every 5 secondsm then you need to modify the @Scheduled annotation sued above as: @Scheduled(fixedRate = 5000). If you want a time gap of 5 seconds between the end of previous execution and the start of the succeeding execution then you need to modify the @Scheduled annotation as @Scheduled(fixedDelay= 5000). You can also trigger a cron job and define your cron parameters say try running a job at 6.30pm everyday by using @Scheduled(cron="30 18 * * * ")
  8. And that is it! You deploy the application. You would see the 'Invoking testTask at " + current date' getting printed every 5 seconds in your console!
  9. Further, Even if you want to update the db, you can go ahead and get the current session and process this method within a transaction to make db updates on a periodical basis.
  10. Note: Remember that the methods annotated with @Scheduled should not have parameters passed to them. They should not return any values too.If ever you want the external objects to be used within your @Scheduled methods, you should inject them into the MyScheduler class using autowiring rather than passing them as parameters to the @Scheduled methods
If you find the information pretty helpful, I would really be happy if you would keep me posted via the comments form displayed under this article! If you had wanted some other information related to the same topic, I would suggest you to drop a note to me using the comments form for that would help me in getting back to you with the details you are in need of!Technorati Tags: , , , , ,

19 comments:

  1. Nice tutorial. It works really easy.

    But I have a question about managing the scheduled tasks. Did you find out, how to list all Tasks and manage them. Means, to change the Cronjob or other scheduled parameter.

    I used the scheduled task at work, and definied the for the annotation driver tag an executer and scheduler like this:




    Is the annotation-driven tag possible without any information about the Excecuter?

    ReplyDelete
  2. Hi Woo,

    The code snippet you posted is not visible. So, can you please add the code snippet related to your annotation driven tag?

    If I understand your query right, changes in the parameter values or the cron job details would require a recompilation.

    But, if you can give me further inputs, I would surely let you know further details!

    ReplyDelete
  3. Hi Woo

    The annotation-driven tag can be incorporated without any other details - This would be simplest implementation!

    ReplyDelete
  4. Good moring,

    just another try :) :

    The additional tags in the applicationContext.xml I use are:





    Ok, I have to try it also with no addition parameter.

    The other question is like you just supposed: If my applicationcontext is runnig, eg. on an application server, and I want to change the Cronjob paramaters on the fly. The only way I now see is to redeploy the project.

    ReplyDelete
  5. Oh, here is the enty again:

    < task:annotation-driven executor="myExecutor" scheduler="myScheduler" />
    < task:executor id="myExecutor" pool-size="5" />
    < task:scheduler id="myScheduler" pool-size="10" />

    The problem ist I have to add a space after the tag opening. Otherwise the editor didn't accept the text.

    ReplyDelete
  6. @Woo

    Sorry! am bak!!!I know that there has been a delay in responding! I apologize for that! Was away!

    First, I dont think you can change the cron params on the fly. That needs recompilation! I welcome suggestions from you readers too in this regard!

    Secondly, Take out the additional parameters (executor="myExecutor" scheduler="myScheduler) and give it a try! Let me know how it works!

    ReplyDelete
  7. @Woo

    Cron job can be set dynamically by using
    @Scheduled(cron = "${config.cronString}")
    public void runJob() {
    System.out.println("Running the scheduled job");
    }

    ReplyDelete
  8. One quick question i used the scheduler by looking at your post but my question is how does it handle in case the previous schdule job not finished and another job need to be fired means

    Lets say i set the scheduler for 5 min and do something in my code lets say it as task1.
    By the time schduler kickoff for next 5 min if my taks1 is not finished and it supposed to start task2 what will happen ..? will it stop task1..?

    ReplyDelete
  9. can u please giv an sample on using @Scheduled(cron = "${config.cronString}").

    i need to put my schedular timer at an external file instead of putting in inside my codes.

    ReplyDelete
  10. thanks man, it made my life easy

    ReplyDelete
  11. Very good write-up on scheduled tasks. I would like to be able to access services from my scheduled task. My services are all defined as beans with scope="session". When I try to access those beans I get the error:

    2010-08-24 23:55:32,752 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[localhost].[/starmount-mobilePos]] Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.scheduling.support.MethodInvokingRunnable#0': Cannot resolve reference to bean 'tenderReversalProcessor' while setting bean property 'targetObject'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tenderReversalProcessor' defined in class path resource [config/application/applicationContext.xml]: Cannot resolve reference to bean 'offlineHandler' while setting bean property 'offlineHandler'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'offlineHandler': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.

    I have been able to make this work by changing my service beans to scope="singleton" but that causes problems when there are multiple concurrent connections. I have also ensured that I have started the RequestContextListener.

    Any insight would be appreciated.

    Thanks,
    Greg

    ReplyDelete
  12. @wonderlife

    Hey - the following works with Spring 3.0.1

    @Scheduled(cron = "${propertyname}") should be used.

    In this case,



    ..


    and in your app.properties you should have

    propertyname=STRING VALUE

    ReplyDelete
  13. @Scheduled(cron = "${propertyname}") should be used.

    In this case,

    <util:properties id="aplnProps" location="/WEB-INF/classes/properties/app.properties" />
    <context:property-placeholder properties-ref="aplnProps" />
    ..
    <task:annotation-driven />

    and in your app.properties you should have

    propertyname=STRING VALUE

    ReplyDelete
  14. yogesh chavan vaijapurApril 6, 2012 at 4:11 AM

    very useful .. a quick and easy description..

    thnks..

    ReplyDelete
  15. It is really useful information.
    My question is, if constructor in java class expect an argument then how can we pass the argument through xml file?

    ReplyDelete
  16. Thank u very much for the tutorial

    ReplyDelete
  17. Hi, I was wondering if you know anything about dynamic job scheduling. e.g. User gets an option to set cron parameter for a particular task?

    Suppose Application has TASK X with certain parameters. User comes and feeds the parameters for TASK X but wants it to be performed periodically so s/he also wants that to be performed in terms of job,, any ideas?

    ReplyDelete