Quartz Calendar Example

0

Quartz calendar is different from java calendar in the sense that they are meant to exclude times rather than define actual firing times.

We use trigger to define the firing time but when it comes to actual firing of a job it depends on the associated calendar.

The calendar will determine whether the firing time is included by the calendar or not. If the calendar ends up excluding the trigger then the job will not fire. For example, a job set to fire every day at 5:00 in the morning can be prevented from firing on holidays by associating the trigger with a holiday calendar.

Exclude Calendar Time Range

When the scheduler has decided to fire the trigger, it validates the firing time against the associated calendar to make sure that the given time is not excluded by the calendar.

Exclude calendar time range

Exclude Calendar Time Range

We use CountDownLatch to define number of times a job should fire in our example.

SimpleJob:

package com.javarticles.quartz;

import java.util.Date;
import java.util.concurrent.CountDownLatch;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class SimpleJob implements Job {

    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        String triggerName = (String) jobDataMap.get("triggerName");
        if (triggerName != null) {
            System.out.println("Tigger " + triggerName + ", fired at " + new Date());
        }
        CountDownLatch c = (CountDownLatch) jobDataMap.get("countDown");
        if (c != null) {
            c.countDown();
        }
    }

}

In the below example we use DailyCalendar to exclude a specified time range each day. The trigger will not be allowed to fire if it falls in the defined time range.
We first need to add the calendar to the scheduler.

scheduler.addCalendar("dayCalendar", dayCalendar, true, true);

Next, use the same calendar name to associate it with the trigger.

 TriggerBuilder
                .newTrigger()
...
                .modifiedByCalendar(calendarName)
...                            
                .build();

Triggers:

package com.javarticles.quartz;

import static org.quartz.DateBuilder.futureDate;
import static org.quartz.DateBuilder.IntervalUnit.SECOND;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;

import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;

public class Triggers {

    public static Trigger fireAfterEvery2Seconds(String  calendarName) {        
        SimpleTrigger trigger = TriggerBuilder
                .newTrigger()
                .withIdentity("trigger1", "group")
                .modifiedByCalendar(calendarName)
                .withSchedule(simpleSchedule()
                                .withIntervalInSeconds(2)
                                .repeatForever())                              
                .build();
        return trigger;
    }
    
}

QuartzTriggerExcludeTimeRangeExample:

package com.javarticles.quartz;

import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Logger;

import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.calendar.DailyCalendar;

public class QuartzTriggerExcludeTimeRangeExample {
    public static void main(String[] args) throws SchedulerException {
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        scheduler.start();
        try {
            CountDownLatch c = new CountDownLatch(3);
            JobDataMap jobDataMap = new JobDataMap();
            jobDataMap.put("triggerName", "fireAfterEvery2Seconds");
            jobDataMap.put("countDown", c);
            JobDetail jobDetail = JobBuilder.newJob(SimpleJob.class)
                    .usingJobData(jobDataMap).withIdentity("job1", "group")
                    .build();

            Calendar cal = Calendar.getInstance();
            cal.setTime(new Date());
            int hrs = cal.get(Calendar.HOUR_OF_DAY);
            int mins = cal.get(Calendar.MINUTE);
            int secs = cal.get(Calendar.SECOND);
            int mills = cal.get(Calendar.MILLISECOND);

            DailyCalendar dayCalendar = new DailyCalendar(hrs, mins-1, secs,
     >               mills, hrs, mins+1, secs, mills);
            LOGGER.info("Excluded from " + hrs + ":" + (mins-1)
                    + ":" + secs + ":" + mills + ":" + "-"
                    + hrs + ":" + (mins+1) + ":" + secs + ":"
                    + mills);

            LOGGER.info("start trigger " + jobDataMap.getString("triggerName"));
            scheduler.addCalendar("dayCalendar", dayCalendar, true, true);
            scheduler.scheduleJob(jobDetail,
                    Triggers.fireAfterEvery2Seconds("dayCalendar"));
            try {
                c.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        } catch (SchedulerException e) {
            e.printStackTrace();
        } finally {
            try {
                scheduler.shutdown();
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
        }

    }

    private static Logger LOGGER = Logger
            .getLogger(QuartzTriggerExcludeTimeRangeExample.class
                    .getSimpleName());
}

Output:

19:10| INFO | StdSchedulerFactory.java 1184 | Using default implementation for ThreadExecutor
19:10| INFO | SimpleThreadPool.java 268 | Job execution threads will use class loader of thread: main
19:10| INFO | SchedulerSignalerImpl.java 61 | Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
19:10| INFO | QuartzScheduler.java 240 | Quartz Scheduler v.2.2.1 created.
19:10| INFO | RAMJobStore.java 155 | RAMJobStore initialized.
19:10| INFO | QuartzScheduler.java 305 | Scheduler meta-data: Quartz Scheduler (v2.2.1) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.

19:10| INFO | StdSchedulerFactory.java 1339 | Quartz scheduler 'DefaultQuartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
19:10| INFO | StdSchedulerFactory.java 1343 | Quartz scheduler version: 2.2.1
19:10| INFO | QuartzScheduler.java 575 | Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
Jun 25, 2016 7:10:02 PM com.javarticles.quartz.QuartzTriggerExcludeTimeRangeExample main
INFO: Excluded from 19:9:2:191:-19:11:2:191
Jun 25, 2016 7:10:02 PM com.javarticles.quartz.QuartzTriggerExcludeTimeRangeExample main
INFO: start trigger fireAfterEvery2Seconds
Tigger fireAfterEvery2Seconds, fired at Sat Jun 25 19:11:02 IST 2016
Tigger fireAfterEvery2Seconds, fired at Sat Jun 25 19:11:04 IST 2016
Tigger fireAfterEvery2Seconds, fired at Sat Jun 25 19:11:06 IST 2016
19:11| INFO | QuartzScheduler.java 694 | Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down.
19:11| INFO | QuartzScheduler.java 613 | Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED paused.
19:11| INFO | QuartzScheduler.java 771 | Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete.

Include Calendar Time Range

The property invertTimeRange defines whether the times specified in the calendar is to be included or excluded. By default it is set to false which means the time range defined in the calendar will be used to exclude the trigger. If invertTimeRange is true, the time range is inverted, that is, all times outside the defined time range are excluded.

Include Calendar Time Range

Include Calendar Time Range

QuartzTriggeIncludeTimeRangeExample:

package com.javarticles.quartz;

import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.calendar.DailyCalendar;

public class QuartzTriggeIncludeTimeRangeExample {
    public static void main(String[] args) throws SchedulerException {
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        scheduler.start();
        try {
            CountDownLatch c = new CountDownLatch(5);
            JobDataMap jobDataMap = new JobDataMap();
            jobDataMap.put("triggerName", "fireAfterEvery2Seconds");
            jobDataMap.put("countDown", c);
            JobDetail jobDetail = JobBuilder.newJob(SimpleJob.class)
                    .usingJobData(jobDataMap).withIdentity("job1", "group")
                    .build();

            Calendar cal = Calendar.getInstance();
            cal.setTime(new Date());
            int hrs = cal.get(Calendar.HOUR_OF_DAY);
            int mins = cal.get(Calendar.MINUTE);
            int secs = cal.get(Calendar.SECOND);
            int mills = cal.get(Calendar.MILLISECOND);

            DailyCalendar dayCalendar = new DailyCalendar(hrs, mins, secs,
                    mills, hrs, mins, secs + 8, mills);
            dayCalendar.setInvertTimeRange(true);
            LOGGER.info("Include from H: " + hrs + ", M: " + mins
                    + ", S: " + secs + ", MILLI: " + mills + " to " + " H: "
                    + hrs + ", M: " + mins + ", S: " + secs + 6 + ", MILLI: "
                    + mills);

            LOGGER.info("start trigger " + jobDataMap.getString("triggerName"));
            scheduler.addCalendar("dayCalendar", dayCalendar, true, true);
            scheduler.scheduleJob(jobDetail,
                    Triggers.fireAfterEvery2Seconds("dayCalendar"));
            try {
                c.await(15, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        } finally {
            try {
                scheduler.shutdown();
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
        }

    }

    private static Logger LOGGER = Logger
            .getLogger(QuartzTriggeIncludeTimeRangeExample.class
                    .getSimpleName());
}

Output:

19:14| INFO | StdSchedulerFactory.java 1184 | Using default implementation for ThreadExecutor
19:14| INFO | SimpleThreadPool.java 268 | Job execution threads will use class loader of thread: main
19:14| INFO | SchedulerSignalerImpl.java 61 | Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
19:14| INFO | QuartzScheduler.java 240 | Quartz Scheduler v.2.2.1 created.
19:14| INFO | RAMJobStore.java 155 | RAMJobStore initialized.
19:14| INFO | QuartzScheduler.java 305 | Scheduler meta-data: Quartz Scheduler (v2.2.1) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.

19:14| INFO | StdSchedulerFactory.java 1339 | Quartz scheduler 'DefaultQuartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
19:14| INFO | StdSchedulerFactory.java 1343 | Quartz scheduler version: 2.2.1
19:14| INFO | QuartzScheduler.java 575 | Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
Jun 25, 2016 7:14:19 PM com.javarticles.quartz.QuartzTriggeIncludeTimeRangeExample main
INFO: Include from H: 19, M: 14, S: 19, MILLI: 298 to  H: 19, M: 14, S: 196, MILLI: 298
Jun 25, 2016 7:14:19 PM com.javarticles.quartz.QuartzTriggeIncludeTimeRangeExample main
INFO: start trigger fireAfterEvery2Seconds
Tigger fireAfterEvery2Seconds, fired at Sat Jun 25 19:14:19 IST 2016
Tigger fireAfterEvery2Seconds, fired at Sat Jun 25 19:14:21 IST 2016
Tigger fireAfterEvery2Seconds, fired at Sat Jun 25 19:14:23 IST 2016
Tigger fireAfterEvery2Seconds, fired at Sat Jun 25 19:14:25 IST 2016
19:14| INFO | QuartzScheduler.java 694 | Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down.
19:14| INFO | QuartzScheduler.java 613 | Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED paused.
19:14| INFO | QuartzScheduler.java 771 | Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete.

Download the source code

This was an example about Quartz Calendars. We have seen how to use the Quartz Calendars to include and exclude time range.

You can download the source code here: quartzSimpleTriggerCalendarExample.zip

About Author

Ram's expertise lies in test driven development and re-factoring. He is passionate about open source technologies and loves blogging on various java and open-source technologies like spring. You can reach him at [email protected]

Comments are closed.