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:

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:

 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:

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:

<?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>

About Pascal Alma

Pascal is a senior IT consultant and has been working in IT since 1997. He is monitoring the latest development in new technologies (Mobile, Cloud, Big Data) closely and particularly interested in Java open source tool stacks, cloud related technologies like AWS and mobile development like building iOS apps with Swift. Specialties: Java/JEE/Spring Amazon AWS API/REST Big Data Continuous Delivery Swift/iOS
This entry was posted in Spring Framework and tagged , . Bookmark the permalink.

7 Responses to Scheduling with Spring and Quartz

  1. jinfrics says:

    post the whole code!

  2. 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! 🙂

  3. Martha says:

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

    😀

    Thx againn

  4. 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

  5. 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

  6. 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 😉

  7. upendra says:

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

Comments are closed.