Search This Blog

Wednesday, 20 February 2013

Scheduling with Annotations

In my previous post we saw how to achieve scheduling using Quartz API and Spring. The thing about Quartz is that it adds an additional dependency in your project.If you have a simple project and do not need Quartz's heavy power, you can use Spring's own scheduling mechanism. I modified our previous example to use it.
Consider the java class:
public class DisplayMessageTask {

    private MessageAgent messageAgent;
    private String userName;

    @Scheduled(cron = "*/2 * * * * ?")
    public void displayMessage(){
        Date sysDate = new Date();
        DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
        String time = dateFormat.format(sysDate);
        System.out.println("Hi "+ userName + ", displaying message at " 
             + time + " message is : " + messageAgent.getMessage());
    }
    //setter getters
}
The class includes a displayMessage method that we need to schedule for every 2 seconds.
Accordingly the annotation has been placed on the displayMessage() method. It includes a cron expression indicating the triggering frequency. From the Spring documentation:
the methods to be scheduled must have void returns and must not expect any arguments. If the method needs to interact with other objects from the Application Context, then those would typically have been provided through dependency injection.
In the previous example we created a job for the class, a trigger for the scheduling frequency and then included it in our scheduler. Here all that is avoided. Simply mark the method of the bean you need to schedule with the Scheduled annotation. 
The annotation provides three techniques for setting an execution schedule
  • cron : The attribute must be supplied with a cron expression indicating the schedule.
  • fixedDelay: In milliseconds, it indicates a fixed period between the end of the last invocation and the start of the next. 
  • fixedRate: In milliseconds, it will execute the method every 'x' milliseconds where 'x' is the value of the attribute. 
Exactly one of these attributes must be configured for the annotation to work correctly. The next step is configuring our scheduler:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:task="http://www.springframework.org/schema/task"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/task
           http://www.springframework.org/schema/task/spring-task-3.0.xsd">

    <task:annotation-driven />
        
    <bean id="displayMessageTask" class ="com.test.DisplayMessageTask">
        <property name="userName" value="Robin"/>
        <property name="messageAgent">
            <bean class ="com.test.MessageAgent"/>
        </property>
    </bean>
</beans>
I have added the bean. Also added an <annotation-driven> element. This is from the task namespace. It causes Spring to scan the beans for any tasks that need to be scheduled. That's it. We are done.
Now to run our code:
public static void main(String[] args) {
    ApplicationContext applicationContext = new 
        ClassPathXmlApplicationContext("spring-task.xml");
    System.out.println("Opened the applicationContext, " + applicationContext.getId());
}
The logs indicate the result:
336  [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanF
actory  - Creating shared instance of singleton bean 'taskScheduler'
352  [main] DEBUG org.springframework.beans.CachedIntrospectionResults  - Gettin
g BeanInfo for class [org.springframework.scheduling.concurrent.ThreadPoolTaskSc
heduler]
...
Opened the applicationContext, org.springframework.context.support.ClassPathXmlA
pplicationContext@272d7a10
Hi Robin, displaying message at 13:53:48 message is : What the heck !!
Hi Robin, displaying message at 13:53:50 message is : This rocks
Hi Robin, displaying message at 13:53:52 message is : This is done
But where is the scheduler ?? 
The task element has an optional scheduler attribute. It specifies the scheduler instance to use when invoking scheduled methods. If no reference is provided, a TaskScheduler backed by a single thread scheduled executor will be used.
The code indicated that a ThreadPoolTaskScheduler was created of pool size 1. Or all the tasks were executed using a single thread. If we do not want this default behavior than we need to provide our own Scheduler instance.
<task:annotation-driven scheduler="taskScheduler"/>
<task:scheduler id="taskScheduler" pool-size="2"/>
Here we have set up a Scheduler with capability to use two threads for scheduling tasks.
Spring uses classes from the java concurrency package to achieve it's scheduling.

3 comments:

  1. @Robin Varghese, how much maximum time we can schedule with Annotations? And on what standard time, the server performance will be best?

    ReplyDelete
  2. Hi,
    With annotations time can mean several things -
    at what exact time should the task run ? (or)
    how often should the task be started ? (or)
    after what interval should the task be run again after completion ?
    I do not see how to give a numerical answer to your question :)

    As with regards to performance, that would depend on several factors both external and internal to your application. For performance concern, I suggest you get the task running as per your application requirements before you try to optimize it.
    Hope this helps.

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete