Scheduling with Spring and Quartz

Just a small post about something I noticed in Quartz. I wanted to offer the user a screen with functionality so they could (re)schedule a batch job which then would do all kinds of batch stuf. To schedule the job in my application I used the following Java code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public void resetScheduleJob() throws CustomSystemException {
 
      StdScheduler scheduler = (StdScheduler)MyQueueContext.getContext().getBean("scheduler");
 
      CronTriggerBean trigger = (CronTriggerBean)MyQueueContext.getContext().getBean("batchProducerTrigger");
 
      try {
           trigger.setCronExpression(getCronExpression());
 
           scheduler.scheduleJob(trigger.getJobDetail(), trigger);
 
      } catch (ParseException pe) {
         throw new CustomSystemException (pe);
      } catch (SchedulerException se) {
         throw new CustomSystemException (se);
      }
      LOG.info("Next run will be at: " + trigger.getNextFireTime());
   }
}
private String getCronExpression() {
 
      // Small trick to get the hours of the day correct.
      String hours = null;
      if (startTime > endTime) {
         hours = endTime + "-23,0-" + startTime;
      } else if (startTime < endTime) {
         hours = startTime + "-" + endTime;
      } else {
         hours = "0";
      }
      return "0 0/" + interval + " " + hours + " ? * " + days;
   }


So this code runs fine the first time when it is called. But when you call it the second time (after the job exists) you will get:

org.quartz.ObjectAlreadyExistsException: Unable to store Job with name: 'batchProducerJob' and group: 'DEFAULT', because one already exists with this identification.

So I rewrote my code to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 public void resetScheduleJob() throws CustomSystemException {
      StdScheduler scheduler = (StdScheduler)MyQueueContext.getContext().getBean("scheduler");
 
      CronTriggerBean trigger = (CronTriggerBean)MyQueueContext.getContext().getBean("batchProducerTrigger");
 
      try {
         trigger.setCronExpression(getCronExpression());
 
         scheduler.rescheduleJob(job.getName(), job.getGroup(), trigger);
 
         scheduler.start();
 
      } catch (ParseException pe) {
         throw new CustomSystemException (pe);
      } catch (SchedulerException se) {
         throw new CustomSystemException (se);
      }
      LOG.info("Next run will be at: " + trigger.getNextFireTime());
   }

Now this code won’t fail if you call it several times….. Actually, it doesn’t do anything! I would expect an exception if the scheduler must have an existing job before it can reschedule one. But nothing there, I only found out that my job wasn’t started at all. So the trick is to check if there is a job with the same name, and if one is found, do the reschedule, otherwise the ‘normal’ schedule. Since I wasn’t expecting this behaviour I thought may be there are more persons running into this so here is the solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void resetScheduleJob() throws CustomSystemException {
      StdScheduler scheduler = (StdScheduler)MyQueueContext.getContext().getBean("scheduler");
 
      CronTriggerBean trigger = (CronTriggerBean)MyQueueContext.getContext().getBean("batchProducerTrigger");
 
      try {
         trigger.setCronExpression(getCronExpression());
 
         JobDetail job = scheduler.getJobDetail(trigger.getJobDetail().getName(), trigger.getJobDetail().getGroup());
         LOG.debug("Job found = " + job);
 
         if (job == null) {
           scheduler.scheduleJob(trigger.getJobDetail(), trigger);
         } else {
            scheduler.rescheduleJob(job.getName(), job.getGroup(), trigger);
         }
 
         scheduler.start();
 
      } catch (ParseException pe) {
         throw new CustomSystemException (pe);
      } catch (SchedulerException se) {
         throw new CustomSystemException (se);
      }
      LOG.info("Next run will be at: " + trigger.getNextFireTime());
   }

And to make this example complete here is my Spring configuration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans SYSTEM "../dtd/spring-beans.dtd">
<beans>
	<bean id="batchProducerJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	  <property name="targetObject" ref="batchProducer" />
	  <property name="targetMethod" value="start" />
	</bean>
	<bean id="batchProducerTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
	    <property name="jobDetail" ref="batchProducerJob" />
        </bean>
	<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
           <!-- intentionally left empty -->
	</bean>
        <bean id="batchProducerScheduler" class="net.pascalalma.batch.schedulers.BatchProducerScheduler">
	    <property name="startTime" value="${batch.produce.starthour}" />
	    <property name="endTime" value="${batch.produce.endhour}" />
	    <property name="interval" value="${batch.produce.interval}" />
	    <property name="days" value="${batch.produce.days}" />
       </bean>
</beans>
tags: ,

About Pascal Alma

Pascal started as an Oracle Developer in 1997 and developed numerous applications with Oracle Designer/Developer and PL/SQL. Since 2001 Pascal becomes more and more active with the development of software at the Java/J2EE platform. Nowadays Pascal is a senior JEE Developer/ Architect and has a lot of experience with several open source initiatives/ frameworks especially within the Enterprise Integration area. Besides these technical skills Pascal is a big Scrum enthusiastic.

9 Responses to Scheduling with Spring and Quartz

  1. Pingback: Pascal’s Blog » Weird Quartz scheduler behaviour

  2. jinfrics says:

    post the whole code!

  3. Martha says:

    Hi there,

    I’m a newbie in quartz and I was confused much about why it wouldn’t update either the job detail nor the trigger on db. I found your blog, and it seems that we have to reschedule it.

    Thanks a lot! :)

  4. Martha says:

    Correction: I found your blog, and now I know that we have to reschedule it.

    :D

    Thx againn

  5. edu says:

    HI there,

    this is a great post, but i have a problem, I need to reschedule an already initiated job.

    in the spring config example above, the scheduler is empty, so no triggers are initiated when the application is deployed, and the rescheduling code works like a charm.
    But when i fill the scheduler bean with two or more triggers, like this:

    applicationContext

    and try to reschedule them, the code doesnt work, and i get no errors in the console.
    I hope theres a solution for this thing and somebody can help me

    thx anyway

  6. edu says:

    oops, i dont know why the code y wanted to upload didnt, so the thing is, that inside the scheduler bean, i added a property tag with the list of 4 triggers on it.
    thanks

  7. Pascal Alma says:

    Hi Edu,
    Posting HTML in the comments can be a possible security risk, unfortunately, I found out in the past.
    If you still want me to look at it you can send the piece of code to blog@pascalalma.net and I will have a look, but I can’t promise anything ;-)

  8. upendra says:

    I think scheduler.rescheduleJob args are trigger name ,trigger group and new trigger object.

  9. upendra says:

    I think scheduler.rescheduleJob args are trigger name ,trigger group and new trigger object.