Before I show how to dynamically create quartz schedulers using Spring, let's quickly review Sping's static configuration. The following excerpts come from "Chapter 23 Scheduling and Tread Pooling" in Spring 2.5.6 reference document and shows how to statically create a quartz scheduler:
<bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="example.ExampleJob"/>
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="exampleJob" />
<!-- run every morning at 6 AM -->
<property name="cronExpression" value="0 0 6 * * ?" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
Spring's JobDetailBean extends quartz's JobDetail and provides the following added functions:
- Setups sensible defaults in its life-cycle init method afterPropertiesSet() e.g. set the job's name to the bean name and set the job's group name to "DEFAULT" if they are empty (Remember that quartz scheduler uniquely identifies a job by its name and group);
- Supports any job class that implements the Runnable interface besides quartz'a regular Job interface;
- If your actual Job class extends Spring's QuartzJobBean, Spring also automatically injects the job map data to the job's properties;
- Setups sensible defaults in its life-cycle init method afterPropertiesSet() e.g. set the trigger's name to the bean name and set the trigger's group name to "DEFAULT" if they are empty (Remember that quartz scheduler uniquely identifies a trigger by its name and group).
- Associates the trigger to the provided's JobDetail in afterPropertiesSet();
- Set the trigger's timezone to the default one and start time to the current time afterPropertiesSet();
- Sets trigger specific job map data through the Java's Map interface;
- Overrides default quartz properties e.g. thread pool parameters and jmx export;
- Registers triggers;
- Registers jobs. If you already assigned a job in your Spring trigger bean, you don't need this;
- Sets the JobFactory to the default Spring's AdaptableJobFactory if it is not set by you. AdaptableJobFactory support Item 2 in the above JobDetailBean's added functions. AdaptableJobFactory's sublass SpringBeanJobFactory also supports the same function as mentioned in Item 3 in the above JobDetailBean's added function.
- Schedules your jobs with quartz's scheduler;
Here are the steps to dynamically configure quartz scheduler using Spring to meet my above requirements.
- Still configures my JobDetailBean, and my actual Job also extends Spring's QuartzJobBean in order to take advantage of the added function;
- Still configures my CronTriggerBean using Spring in order to take advantage of the added function. But its scope should be "prototype" so that I can dynamically retrieve a new trigger bean instance and set its cronExpression and its job map data.
Here is the new trigger configuration:
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean" scope="prototype">
<property name="jobDetail" ref="exampleJob"/>
</bean>
Here is the pseudo code:
CronTrigger ct = (CronTrigger)appCtx.getBean("cronTrigger");
ct.setName(ct.getName() + some-distinguisher);
ct.getJobDataMap().put(some-key, some-value);
ct.setCronExpression(some-exp);
- Still configures my SchedulerFactorBean. But I need to add my above triggers to the scheduler. Because I create the above trigger after SchedulerFactorBean's afterPropertiesSet(), the trigger's associated JobDetail is not added to the scheduler. But I can explicitly register the JobDetail:
Here is the pseudo code:
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false">
<property name="jobDetails">
<list>
<ref bean="exampleJob"/>
</list>
</property>
<property name="triggers"> <!-- will dynamically add more triggers -->
<list/>
</property>
<property name="autoStartup" value="true"/>
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.jmx.export">true</prop>
<prop key="org.quartz.threadPool.threadCount">2</prop>
</props>
</property>
</bean>
Scheduler sched = (Scheduler)appCtx.getBean("scheduler");
sched.scheduleJob(ct);
why throw:
ReplyDeleteInvocation of init method failed; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/modeler/Registry
thanks!
this is easy to fix.
ReplyDeleteYou also enabled the jmx export by including "true" in the config", but your classpath doesn't have the following needed jar:
commons-modeler-version.jar (i am using commons-modeler-2.0.1.jar)
That's cool, but how to change the thread pool size dynamically?
DeleteFinally. I've looking for this for hours. A million thanks Yong!
ReplyDeleteThank you! This helped me too.
ReplyDelete