Dynamic Job Scheduling Using Quartz Scheduler and RRule

Quartz SchedulerSchedulers play a key role of running batch processes in software applications. As a matter of fact Java language supports several key frameworks that assist software developers in dynamic job scheduling. Fulcrum Scheduler, Oddjob Scheduler, JDRing, Quartz Scheduler and J2EE Scheduler are some of the popular job schedulers. Out of all these, Quartz Scheduler is the most widely used job scheduler, as it offers impressive features including dynamic job scheduling and it is comparatively easier to use than other job schedulers.

Quartz is an advanced and powerful dynamic job scheduling framework (open source). It allows software developers to schedule jobs at a desired time. Furthermore, it provides provisions to edit, pause/resume and delete the scheduled job. Utilizing Quartz, software developers can create simple as well as complex schedules to execute thousands of jobs.

In this blog post, we will learn ‘How to schedule a job dynamically using the Quartz Scheduler’. Subsequently, we will also figure out the process to edit, pause/resume and delete scheduled jobs. We have considered a use case, which helps readers easily understand the process of scheduling jobs using Quartz Scheduler. As a software developer, if you are required to schedule jobs on daily, weekly, monthly or yearly basis, it is possible to execute the same using Quartz scheduler.

Job Schedule Frequencies

Below are some of the job schedule frequencies that can be setup using Quartz Scheduler:

  1. Daily
  2. Weekly
  3. Monthly
  4. Yearly

Job Schedule Recurrence Options

Quartz Scheduler further allows software developers to define the end recurrence options. Below are the three end recurrence options available in Quartz:

  1. Never End
  2. End After <No. of Occurrences>
  3. End By <Particular Date>

Appointment Recurrence Schedule - Quartz Scheduler

Solution Approach

In order to create the above job schedule frequencies with the mentioned end recurrence, software developers have to create a user interface using jQuery’s recurrenceinput.js library, Google’s RFC RRULE jar and Quartz Job Scheduler API. The recurrenceinput.js library captures the user selection in the form of RRULE syntax. The recurring schedule is captured as mentioned below:

RRULE:FREQ=WEEKLY;BYDAY=MO,WE;COUNT=4

Using Google’s RFC RRULE API, developers can parse the recurrent rule(rrule) and subsequently, by making use of custom logic convert it to Quartz’s CRON expression. Below is a step-by-step guide that would help developers achieve the above mentioned activity:

Step 1- How to Configure Quartz Scheduler?

The below code helps developers to configure the Quartz Scheduler:

<bean id="schedulerFactoryBean"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="applicationContextSchedulerContextKey">
<value>applicationContext</value>
</property>
<property name="configLocation" value="classpath:quartz.properties" />
<property name="overwriteExistingJobs" value="true" />
</bean>

applicationContextSchedulerContextKey – This property is used to pass a message to the Quartz Scheduler i.e. how and where to find Spring’s applicationContext. Furthermore, while executing a job, the Quartz Scheduler can access Spring beans using scheduler context. Here’s the code:

ApplicationContext appContext= jobExecutionContext.get(“applicationContextSchedulerContextKey”)

quartz.properties – This function is used to define all Quartz Scheduler properties; here are few examples of these properties (jobStore, datasource, threadPool, plugin, etc.).

Step 2 – How to Implement a Quartz Scheduler Job?

In order to implement a Quartz Scheduler Job, developers should make use of the Job class to implement the execute(JobExecutionContext) method of the Quartz Job interface. The Job class throws JobExecutionException, if an error occurs during job execution, JobExecutionContext should be populated with all the required information to run the job, while adding/editing a job. Below is the sample job code, which would help us understanding the concept better:

Public class MyJob implements Job{
@Override
 public void execute(final JobExecutionContext context)throws JobExecutionException {
 JobDetail jobDetail = context.getJobDetail();
 Long reportIdToRun =jobDetail.getKey();
 // do the actual job logic here. May be running a report, sending an
 // email, purging file system directory, etc.
 }
 }

Step 3 – How to Add a New Job to Quartz Scheduler?

To add a new job to the Quartz Job Scheduler for dynamic job scheduling, developers have to make use of JobDetail and Trigger components. The JobDetail interface is created using a unique key and a Job class, whereas the Trigger component is created using a unique trigger key. Lastly, schedules are created using TriggerBuilder with start and end dates. Here’s a sample job code that helps us gain a better understanding on the process of adding a new job schedule:

scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = reportIdToRun; //
JobDetail jobDetail= newJob(MyJob.class).withIdentity(jobKey).build();
Trigger trigger= newTrigger().withIdentity(triggerKey).withSchedule(cronSchedule(cronExpression)).startAt(startDate).endAt(endDate).build()
scheduler.scheduleJob(jobDetail, trigger);

We have learnt the process of adding a job to Quartz Scheduler in three simple steps. Now, let’s figure out the process of editing, pausing, and deleting existing jobs in Quartz Scheduler.

How to Edit an Existing Job?

In order to edit/update an existing job in Quartz Scheduler developers need to:

  1. Update the job details
  2. Update the trigger details

Here’s the code, which would allow developers to complete this activity:

scheduler.addJob(updatedJobDetail, true, true); // 2nd parameter true means updating the existing job with the updated one.
scheduler.rescheduleJob(oldTriggerKey, newTrigger);
How to Pause/Resume an Existing Job?

In order to Pause/Resume an existing job in Quartz Scheduler, developers can use the below code:

scheduler.pauseJob(jobKey);
scheduler.resumeJob(jobKey); // the job will be resumed based on the quartz misfire instructions.
How to Delete an Existing Job?

Deleting an existing job in Quartz Scheduler is a fairly easy task. Below is the code that would allow developers to delete an existing job:

  • Unschedule the job
  • Delete the job
scheduler.unscheduleJob(jobKey);
scheduler.deleteJob(jobKey);

Technology Stack

To demonstrate the functioning of Quartz Scheduler, we have utilized the below tools and technologies:

  • Quartz 2.2.1
  • Spring Framework 3.2.12
  • jQuery recurrence widget (recurrenceinput.js)
  • Google’s iCalendar RFC 2445

Conclusion

By and large, Quartz Scheduler has gained massive popularity among Java developers due to its flexibility and dynamic scheduling capabilities. No wonder, it is one of the most preferred job schedulers for dynamic job scheduling. I assume this blog would help Java developers to gain a good understanding of Quartz Scheduler for dynamic job scheduling. If you have any queries or inputs, please feel free to post your comments.

Bala Gangadhar Tilak Mamidipalli

View posts by Bala Gangadhar Tilak Mamidipalli
Tilak MBG is a Java programmer at Evoke Technologies. He has 14 years of experience in designing and developing client server and web based applications using Java and J2EE technologies. Tilak likes reading about new technologies during his free time.

62 Comments

  1. Its really helpful for me. thank you for the blog. could you please share me sample code please my mail id sindhu2953(at)gmail(dot)com.

  2. Could you please share some sample code with UI for reference. my mail id : v(dot)krishna(dot)kalluri(at)gmail(dot)com

      1. Hi Krishna,

        Did you get the code if you got it can you please send it to me. my mail id sindhu2953(at)gmail(dot)com

  3. Hi Bala,

    I am designing similar scheduling. Could you please share the code with me if possible so I need not work much on this.

    mail id: Sanjeeva.potru(at)gmail(dot)com

    Thanks,
    Sanjeeva Reddy

  4. Hi Tilak,

    Nice Article!!!
    Can you share sample code on email : kadav.kiran(at)gmail(dot)com or any repository location if it anywhere on GIT or SVN.

  5. Hi
    Thanks for the informative post. Can I validate if the task execution time is updated everytime it is called, and reschedule it during the call? So all the tasks details are saved in DB at my end and the db entries can be updated from a different web based app.
    Thus, I will have to reschedule jobs on the fly based on the entries in database. (Maybe using a flag in db, so that I know which one is updated)

  6. Nice blog,very helpful for me sir.PLease can you send me your source code
    my email @: wajdibhh(at)gmail(dot)com

    thks in advance

  7. Hi,
    Can you please share your Dynamic Job Scheduling Using Quartz Scheduler code with me?
    My email id is renukamjadhav(at)gmail(dot)com

    Thanks!

  8. Hi,

    Do you have a sample project on Spring boot which should use quartz scheduler for scheduling job(>1) and job related details will be in .yml file. Basically, how to trigger a job from spring boot application. Any working code will help me a lot.

    Thanks in advance.

  9. Nice blog and Thank you for a very informative post Tilak. Can you please share the code with me, my email id is rakeshreddy6843(at)gmail(dot)com

  10. Hi, Your article is very nice and helpful.

    Could you please share the code with me sampath(at)edify(dot)in ?

  11. Hi Tilak,

    I am developing Content Management System Project in Springs and hibernate which includes sending auto mails.
    Can you suggest me with some sample code of Quartz such that i can explore and implement it as my requirement.
    Please mail me at naveenreddy531cse(at)gmail(dot)com

  12. Hi tilak, could please mail me the project which covers pause/resume, edit and delete
    arshfrndz(at)yahoo(dot)com

  13. Hi Tilak,

    A job is scheduled to fire after 10 secs. Quartz Scheduler automatically unschedules the job before invoking the fire() method. So jobs are not getting executed. This issue seems to happen if we continuously schedule jobs (like more than 10-15 jobs). After 10-15 jobs jobs, no other jobs are getting fired.
    Can you please let me know under what and all situations Quartz Scheduler will unschedule ? This seems to be an abnormal behavior.
    NOTE: We didn’t invoke unschedule any where in our code unless and until it is required. Also, schedules are not deleted manually.
    Please suggest if you have idea.

  14. Very nice blog.
    I have kind of a similar scenario to work upon.
    I want to implement a scheduler, in which one thread will continuously listen to a mysql/oracle database table. Once a new record is inserted into the database table, depending upon one of the column value of my database table, a new thread will be spawned correspondingly in my scheduler which will run periodically.
    Any ideas how can I use quartz in this ?

  15. Hi Tilak,

    Thanks a lot for your Post.
    I have One Query regarding memory Overhead Of multiple Scheduled task.
    will it affect the memory, if we have scheduled more than thousand jobs at a time.

    1. Hi Tanweer,

      It does affect the memory when more no.of jobs are scheduled to run at a time so we need to be cautious about this and tune the thread pool count according to our need. We can throttle the thread pool count dynamically based on our need without restarting the server. Please let me know if you have further comments.

  16. Hello Sir, your article is very nice. But can you send me small Quartz maven project with all these(Start, Stop, Resume, Pause) operations

  17. Interesting article ! I was enlightened by the info ! Does someone know if my assistant would be able to obtain a fillable 2014 IRS 1040 – Schedule A form to fill out ?

  18. Hi Tilak,
    Thanks for the wonderful post. I have been looking for this. Can you please share the source code. It would be really helpful.

    1. Hi Sneha,
      Due to security reasons i can not share the code base with you but i can definitely help you in implementing this feature.

  19. Nice post with a clear explanation about the Quartz Job Scheduling technology for the beginners.

    Thank you for the sharing and expecting more in near future.

    Thanks…!!

  20. This is like a hidden thing which come out through this blog thanks for sharing and this is useful to developers for dynamic schedules using RRule and quartz in projects.

  21. Nice blog Tilak. Its gives more information about the scheduling mechanism using Quartz and RRULE.Thanks for posting.

  22. Nice blog!! Is the custom logic for converting rrule to cron expression generic? If YES, It can be published as a reusable component. Thanks.

    1. Sravan, below is the generic code for converting rrule to cron expression but as mentioned earlier, quartz supports different triggers not just cron so we need a generic implementation from quartz api to integrate quartz with rrule ical.

      import java.text.ParseException;
      import java.text.SimpleDateFormat;
      import java.util.Calendar;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Locale;
      import java.util.Map;

      import org.apache.commons.collections4.CollectionUtils;
      import org.apache.commons.lang3.ArrayUtils;

      import com.google.ical.values.RRule;
      import com.google.ical.values.WeekdayNum;

      public class RRuleToCronExpressionConverter {

      private static final String START_TIME = “startTime”;
      private static final String MONTH = “1/%s”;
      private static final String INTERVAL = “INTERVAL”;
      private static final String HASH = “#”;
      private static final String QUESTION_MARK = “?”;
      private static final String ASTERISK = “*”;
      private static final String SPACE = ” “;
      private static final int CRON_BUILDER_CAPACITY = 50;
      private static final String LAST_DAY_OF_MONTH = “L%d”;
      private static final int DAY_OF_WEEK_BUILDER_CAPACITY = 20;
      private static final SimpleDateFormat HOUR_MIN_FORMAT = new SimpleDateFormat(“h:mm a”, Locale.getDefault());
      @SuppressWarnings(“serial”)
      private static final Map WEEK_MAP = new HashMap() {
      {
      put(“SU”, “SUN”);
      put(“MO”, “MON”);
      put(“TU”, “TUE”);
      put(“WE”, “WED”);
      put(“TH”, “THU”);
      put(“FR”, “FRI”);
      put(“SA”, “SAT”);
      }
      };

      private Calendar getCalendar(final String startTime) {
      Calendar calendar = Calendar.getInstance();
      try {
      calendar.setTime(HOUR_MIN_FORMAT.parse(startTime));
      }
      catch (ParseException e) {
      e.printStackTrace();
      }
      return calendar;
      }

      String getCronExpression(final RRule rRule) {
      StringBuilder cronExpressionBuilder = new StringBuilder(CRON_BUILDER_CAPACITY);

      Calendar calendar = getCalendar(START_TIME);

      cronExpressionBuilder.append(getSeconds(rRule)).append(SPACE).append(calendar.get(Calendar.MINUTE)).append(SPACE)
      .append(calendar.get(Calendar.HOUR_OF_DAY)).append(SPACE);

      switch (rRule.getFreq()) {
      case DAILY:
      cronExpressionBuilder.append(ASTERISK).append(SPACE).append(ASTERISK).append(SPACE).append(QUESTION_MARK).append(SPACE);
      break;
      case WEEKLY:
      cronExpressionBuilder.append(QUESTION_MARK).append(SPACE).append(ASTERISK).append(SPACE).append(getDayOfWeek(rRule))
      .append(SPACE);
      break;
      case MONTHLY:
      cronExpressionBuilder.append(getDayOfMonth(rRule)).append(SPACE).append(getMonth(rRule)).append(SPACE)
      .append(getDayOfWeek(rRule)).append(SPACE);
      break;
      case YEARLY:
      cronExpressionBuilder.append(getDayOfMonth(rRule)).append(SPACE).append(getMonth(rRule)).append(SPACE)
      .append(getDayOfWeek(rRule)).append(SPACE);
      break;
      default:
      break;
      }

      cronExpressionBuilder.append(ASTERISK);
      return cronExpressionBuilder.toString();
      }

      private String getDayOfMonth(final RRule rRule) {
      String dayOfMonth = QUESTION_MARK;
      if (ArrayUtils.isNotEmpty(rRule.getByMonthDay())) {
      if (rRule.getByMonthDay()[0] < 0) {
      dayOfMonth = String.format(LAST_DAY_OF_MONTH, rRule.getByMonthDay()[0]);
      }
      else {
      dayOfMonth = String.valueOf(rRule.getByMonthDay()[0]);
      }
      }
      return dayOfMonth;
      }

      private String getDayOfWeek(final RRule rRule) {
      String dayOfWeek = QUESTION_MARK;
      List weekdayNums = rRule.getByDay();
      if (CollectionUtils.isNotEmpty(weekdayNums)) {
      StringBuilder weeks = new StringBuilder(DAY_OF_WEEK_BUILDER_CAPACITY);
      for (WeekdayNum weekdayNum : weekdayNums) {
      weeks.append(“,”).append(WEEK_MAP.get(weekdayNum.wday.name()));
      if (weekdayNum.num != 0) {
      weeks.append(HASH).append(weekdayNum.num);
      }
      }
      dayOfWeek = weeks.substring(1);
      }
      return dayOfWeek;
      }

      private String getMonth(final RRule rRule) {
      String month = null;
      if (rRule.toIcal().contains(INTERVAL)) {
      month = rRule.getInterval() > 0 ? String.format(MONTH, rRule.getInterval()) : String.format(MONTH, 1);
      }
      else if (ArrayUtils.isNotEmpty(rRule.getByMonth())) {
      month = String.valueOf(rRule.getByMonth()[0]);
      }
      return month;
      }

      private int getSeconds(final RRule rRule) {
      int seconds = 0;
      if (ArrayUtils.isNotEmpty(rRule.getBySecond())) {
      seconds = rRule.getBySecond()[0];
      }
      return seconds;
      }

  23. It helps in understanding clearly about the scheduling mechanisms using Quartz and Google’s RRULE. Thanks for sharing the details, nice blog.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

%d bloggers like this: