15 Ağustos 2010 Pazar

Dynamic Rescheduling by Spring-Quartz

Spring supports Quartz so you can easily create batch jobs. These jobs may be triggered in time intervals or cron expressions. In context xml you can define these attributes. I want to explain how to change dynamically these definitions ,which is read from context xml.

First how to define a quartz job in context.xml ...You have to define JobDetailBean,SimpleTriggerBean and SchedulerFactoryBean :

<bean name="scheduledJob" id="scheduledJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.blogspot.devside.quartz.job.ScheduledJob"/>
</bean>


<bean id="trigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="scheduledJob"/>
<property name="startDelay" ref="2000" />
<property name="repeatInterval" ref="2000"/>
</bean>

<bean id="schedulerFactory" name="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" scope="singleton">
<property name="triggers">
<list>
<ref bean="trigger"/>
</list>
</property>
</bean>



And our ScheduledJob class .It will only print current time in seconds.

public class ScheduledJob extends QuartzJobBean{

static SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");
@Override
protected void executeInternal(JobExecutionContext arg0)
throws JobExecutionException {
System.out.println("Scheduled job - Current Millis : " + format.format(new Date(System.currentTimeMillis())) );
}
}

Then we need Runny class which has a main method.

public class Runny {
public static void main(String[] args) throws InterruptedException {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("/applicationContext.xml");
}
}


If you run the runny class, you will see the result , looks like the following lines.

Scheduled job - Current Millis : 10:51:27
Scheduled job - Current Millis : 10:51:29

Scheduled job - Current Millis : 10:51:31
Scheduled job - Current Millis : 10:51:33
Scheduled job - Current Millis : 10:51:35
...


Now we have a quartz job which runs in every 3 seconds. 3 seconds time interval is defined in context xml . It is static. Now what if we want to retrieve time interval value from a database or another source and change it dynamically.

First , we have to get the value from a bean (bean can get value from database , web service,etc...) ,we will use method invocation for this.
Second , we will change repeatInterval and startDelay . SchedulerFactoryBean will be singleton so we can get trigger and change its values.

Our new context xml will be like the following lines :

<bean id="scheduledJobDef" name="scheduledJobDef" class="com.blogspot.devside.quartz.job.ScheduledJobDef"/>


<bean id="rescheduler" name="rescheduler" class="com.blogspot.devside.quartz.Rescheduler">

<property name="schedulerFactory" ref="schedulerFactory"></property>
<property name="scheduledJobDef" ref="scheduledJobDef"></property>
</bean>

<bean name="scheduledJob" id="scheduledJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.blogspot.devside.quartz.job.ScheduledJob"/>
</bean>


<bean id="trigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="scheduledJob"/>
<property name="startDelay" ref="startDelayMethod" />
<property name="repeatInterval" ref="repeatIntervalMethod"/>
</bean>

<bean id="schedulerFactory" name="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" scope="singleton">
<property name="triggers">
<list>
<ref bean="trigger"/>
</list>
</property>
</bean>

<bean id="repeatIntervalMethod"

class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="scheduledJobDef" />
</property>
<property name="targetMethod">
<value>getFirstRepeatInterval</value>
</property>
<property name="arguments">
<list />
</property>
</bean>

<bean id="startDelayMethod"

class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="scheduledJobDef" />
</property>
<property name="targetMethod">
<value>getFirstStartDelay</value>
</property>
<property name="arguments">
<list />
</property>
</bean>


Scheduled JobDef class :

public class ScheduledJobDef {
public long getFirstStartDelay(){
return 2000;
}

public long getFirstRepeatInterval(){

return 2000;
}

public long getStartDelay(){

return 5000;
}

public long getRepeatInterval(){

return 5000;
}
}



MethodInvocationFactoryBean beans will invoke getFirstStartDelay and getFirstRepeatInterval.

Rescheduler class :

public class Rescheduler {
StdScheduler schedulerFactory;
ScheduledJobDef scheduledJobDef;


public void reschedule(){

System.out.println(schedulerFactory);
try {
SimpleTriggerBean t = (SimpleTriggerBean) schedulerFactory
.getTrigger("trigger", Scheduler.DEFAULT_GROUP);

JobDetail job = schedulerFactory.getJobDetail(t.getJobDetail()

.getName(), t.getJobDetail().getGroup());

t.setRepeatInterval(scheduledJobDef.getRepeatInterval());
t.setStartDelay(scheduledJobDef.getStartDelay());
t.setStartTime(new Date( System.currentTimeMillis()));

schedulerFactory.rescheduleJob(t.getName(), Scheduler.DEFAULT_GROUP, t);

schedulerFactory.start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public StdScheduler getSchedulerFactory() {

return schedulerFactory;
}

public void setSchedulerFactory(StdScheduler schedulerFactory) {

this.schedulerFactory = schedulerFactory;
}

public ScheduledJobDef getScheduledJobDef() {

return scheduledJobDef;
}

public void setScheduledJobDef(ScheduledJobDef scheduledJobDef) {

this.scheduledJobDef = scheduledJobDef;
}

}


In reschedule method , trigger retrieved from singleton SchedulerFactoryBean then its values are changed.Trigger is rescheduled and started.

New lines must be added to Runny class ,

public class Runny {


public static void main(String[] args) throws InterruptedException {

ApplicationContext ctx =
new ClassPathXmlApplicationContext("/applicationContext.xml");

Thread.sleep(10000);


Rescheduler rescheduler = (Rescheduler) ctx.getBean("rescheduler");

rescheduler.reschedule();

Thread.sleep(10000);

}
}


There are two sleep because I want to show difference between before and after trigger rescheduled.
System out will look like the following lines :

Scheduled job - Current Millis : 11:09:46
Scheduled job - Current Millis : 11:09:48
Scheduled job - Current Millis : 11:09:50
Scheduled job - Current Millis : 11:09:52
Scheduled job - Current Millis : 11:09:54
org.quartz.impl.StdScheduler@dc57db
Scheduled job - Current Millis : 11:09:54
Scheduled job - Current Millis : 11:09:59
Scheduled job - Current Millis : 11:10:04
..

As you can see , after reschedule progress is done , time interval changed as 5 seconds.

5 yorum:

  1. Bài post của tác giả quá hữu ích, thank bạn đã share.
    Xem tại website : Giá đá thạch anh vụn

    YanıtlaSil
  2. Bài post của Bạn quá hay, cám ơn bạn đã chia sẻ.
    Xem tại website : Chế tác đá thạch anh

    YanıtlaSil
  3. Bài post của tác giả hay, thank anh đã share.
    Thông tin thêm : Tỳ hưu đá thạch anh

    YanıtlaSil
  4. It's very good tutorial sir, but i have a question. What changes should i make in order to run project if i want to add Tomcat Local Server, because i don't have main class.

    YanıtlaSil
  5. It's very good tutorial sir, but i have a question. What changes should i make in order to run project if i want to add Tomcat Local Server, because i don't have main class.

    YanıtlaSil