6

In my application there is a requirement to be able to create Scheduled Job(s) depending on the type of Request that comes in (Dynamically).

Can I still use Spring to create and trigger Jobs? If Yes, how?

Any help would be useful.

7 Answers 7

12

Given that the SchedulerFactoryBean exposes a native Quartz Scheduler object, you can wire that directly into your controller class, and then dynamically create and register triggers and jobs with the Scheduler object.

Spring itself can't be used for the scheduling of the dynamically created jobs, since Spring's bean support will be used for statically configured jobs, but the native Quartz Scheduler API is reasonable enough to use on its own (barely). As fr triggering of the jobs, that Quartz's job, not Spring's.

edit: either I'm mis-understanding the original question, or everyone else is. The other answers all detail how to statically wire up a series of quartz jobs using Spring, but the question was how to dynamically schedule jobs as requests come in.

1
  • I understand the question the same way. I was also wondering about the relation of the statically defined jobs and jobs that are created later. Commented Jun 9, 2012 at 10:01
4

Look at CronTriggerBean and JobDetailBean. The 'MyJob' class mocked up below is an instance of QuartzJobBean. The cron expression is what you'd expect, but with seconds as its first value.

<beans>
   <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
      <property name="startupDelay" value="5"/>
      <property name="waitForJobsToCompleteOnShutdown" value="false"/>
      <property name="triggers">
         <list>
            <bean class="org.springframework.scheduling.quartz.CronTriggerBean">
               <property name="jobDetail">
                  <bean class="org.springframework.scheduling.quartz.JobDetailBean">
                     <property name="jobClass" value="edu.vt.MyJob"/>
                     <property name="jobDataAsMap">
                        <map>
                           <entry key="messageSource" value-ref="messageSource"/>
                           <entry>
                              <key><value>anotherProperty</value></key>
                              <bean class="edu.vt.MyUsefulBean">
                                 <constructor-arg index="0" value="..."/>
                              </bean>
                           </entry>
                        </map>
                     </property>
                  </bean>
               </property>
               <property name="cronExpression" value="0 * * * * ?"/>
            </bean>
         </list>
      </property>
   </bean>
</beans>
4

There does not seem to be much complete information on this. This is how I schedule jobs dynamically. Of course you could replace the simple trigger with some other trigger.

Spring beans:

<bean name="dailyUpdateJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="com.somecompany.scheduler.DailyUpdates" />
</bean>

<bean id="dailyCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="dailyUpdateJob" />
    <!-- run every morning at 4:15 AM -->
    <property name="cronExpression" value="00 15 04 * * ?" />   
</bean>

<bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="dailyCronTrigger" />
            <ref bean="weeklyReportsCronTrigger" />
        </list>
    </property>
    <property name="applicationContextSchedulerContextKey">
        <value>applicationContext</value>
    </property>
</bean>     

To run the job immediately get a reference to the scheduler and the job, attach a simple trigger and put it into the scheduler, like this:

    @Autowired
    SchedulerFactoryBean scheduler;

    @Autowired 
    @Qualifier("dailyUpdateJob")
    JobDetailFactoryBean dailyJob;

    public void dynamicJobTrigger() throws Exception {
        // Create a trigger for "now" 
        SimpleTrigger trigger = (SimpleTrigger) newTrigger()
                    .startAt(new Date())
                    .forJob(dailyJob.getObject())
                    .build();

        // And drop it into the scheduler for immediate execution
        scheduler.getScheduler().scheduleJob(trigger);
    }   
2

You can also get Spring to trigger methods on your beans using Quartz (i.e. youdon't need to create any Quartz-specific classes at all) using the MethodInvokingJobDetailFactoryBean in the package org.springframework.scheduling.quartz

2

You can download sample source code from this link

<?xml version="1.0" encoding="UTF-8"?>

<!--  scheduler factory -->
<bean   id="com.notary.app.invoicing.scheduler.SchedulerFactory" 
        class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="ASFImportTrigger"/>
        </list>
    </property>
    <property name="dataSource">
        <ref bean="datasource"/>
    </property>
    <property name="transactionManager">
        <ref bean="transactionManager"/>
    </property>
    <property name="quartzProperties">
        <props>
            <prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
            <prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.MSSQLDelegate</prop>
            <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
            <prop key="org.quartz.jobStore.selectWithLockSQL">SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?</prop>
            <prop key="org.quartz.plugin.triggHistory.class">org.quartz.plugins.history.LoggingTriggerHistoryPlugin</prop>
            <prop key="org.quartz.plugin.triggHistory.triggerFiredMessage">Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss dd/MM/yyyy}</prop>
            <prop key="org.quartz.plugin.triggHistory.triggerCompleteMessage">Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss dd/MM/yyyy} with resulting trigger instruction code: {9}</prop>
            <prop key="org.quartz.plugin.jobHistory.class">org.quartz.plugins.history.LoggingJobHistoryPlugin</prop>
            <prop key="org.quartz.plugin.jobHistory.jobSuccessMessage">Job {1}.{0} fired at: {2, date, dd/MM/yyyy HH:mm:ss} result=OK</prop>
            <prop key="org.quartz.plugin.jobHistory.jobFailedMessage">Job {1}.{0} fired at: {2, date, dd/MM/yyyy HH:mm:ss} result=ERROR</prop>
        </props>
    </property>
    <property name="overwriteExistingJobs" value="true"/>
    <property name="startupDelay" value="50"/>
    <property name="applicationContextSchedulerContextKey">
        <value>applicationContext</value>
    </property>
</bean>

1

A year later and I find myself having to something very similar. Googling around, I found this link which describes getting access to the application context from within a scheduled job through the JobExecutionContext. I think I will be creating an abstract type job that can do some of the actual job creation and use a prototype to actual inject required services when the job needs to run.

1

Spring 3 (latest version at time of writing) supports setting up jobs almost completely with annotations.

See: Spring reference on scheduling

Not the answer you're looking for? Browse other questions tagged or ask your own question.