Much like before, we have two key concepts:
- job: a job is a runnable class that implements the Job interface
- trigger: a Trigger is a specific schedule for a job. A trigger can only be linked to a job, and a job can be linked to multiple triggers.
Some things to be mindful about:
1. by default Quartz will generate jobs with NO support for DI. To achieve that we need to thank Brian Matthews and jelies:
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
2. the easiest implementation of Quartz uses an in-memory storage, meaning if the application is restarted or crashes, ALL schedules are lost. You can use a JDBC storage instead for more reliability. You can configure your scheduler in a quartz.properties file, for example minimal settings:
org.quartz.scheduler.instanceName=SOME_NAME
org.quartz.threadPool.threadCount=3
org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
We then create a properties bean:
@Bean
public Properties quartzProperties() {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
Properties properties;
try {
propertiesFactoryBean.afterPropertiesSet();
properties = propertiesFactoryBean.getObject();
} catch (IOException e) {
System.err.println("Cannot load quartz.properties!");
}
return properties;
}
And the scheduler bean:
@Bean
public SchedulerFactoryBean quartzScheduler() {
SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean();
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
quartzScheduler.setJobFactory(jobFactory);
quartzScheduler.setQuartzProperties(quartzProperties());
return quartzScheduler;
}
3. Quartz uses java Calendar and java Date objects for its operations, you can force a system-wide TimeZone by changing the default (for example to set UTC):
import java.util.TimeZone;
TimeZone.setDefault("UTC");
Then we can start the scheduler:
quartzScheduler.start();
4. after a scheduler has been shut down IT CANNOT BE RESTARTED. To shut down the scheduler AND wait for running jobs to complete, use:
quartzScheduler.shutdown(true);
Now we can start our coding, for example creating a job:
//or also .newJob(SomeJob.class)
JobDetail job = JobBuilder.newJob(Class.forName("SOME_JOB").asSubclass(Job.class))
.withIdentity("SOME_NAME", "SOME_GROUP") //unique identifier for this job
.usingJobData(new JobDataMap()) //a HashMap-like structure containing data that every schedule running this job will have
.build();
And our job class might look like this (we inject the scheduler just for testing, it is not necessary):
public class SampleJob implements Job {
@Autowired
private Scheduler quartzScheduler;
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
JobDataMap dataMap = context.getMergedJobDataMap();
System.out.println("Sample job! From scheduler: " + quartzScheduler.getSchedulerName());
for(Map.Entry<String, Object> data : dataMap.entrySet()) {
System.out.println("Got: " + data.getKey() + ": " + data.getValue());
}
} catch (SchedulerException e) {
System.err.println("BAD LUCK");
}
}
}
then we create a simple schedule for it:
//a trigger that runs forever every second and on a misfire it runs immediately, then continues on normal schedule
ScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
.repeatForever() //this trigger will run forever
.withMisfireHandlingInstructionFireNow() //you can set whatever misfire handling you need here
.withIntervalInSeconds(1); //also you can configure as you need here
or a more complex schedule:
//a trigger that runs every day according to "onDaysOfTheWeek", it is a set of integers representing the active days, with Monday = 1
//on a misfire it runs immediately, then continues on normal schedule
//it starts every day at the specified time (FIRST execution here)
//it stops every day at the specified time (LAST execution here). This is NOT a cron expression in format FROM-TO, because in cron, the last execution (TO) would NOT happen
//running with a frequency of 1 second
ScheduleBuilder scheduleBuilder = DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule()
.withMisfireHandlingInstructionFireAndProceed()
.startingDailyAt(TimeOfDay.hourAndMinuteFromDate(new Date()))
.endingDailyAt(TimeOfDay.hourAndMinuteFromDate(new Date()))
.withIntervalInSeconds(1)
.onDaysOfTheWeek(new HashSet<Integer>());
We can now create a trigger for that job with our schedule:
Trigger trigger = newTrigger()
.withIdentity("SOME_NAME", "SOME_GROUP") //unique identifier for this trigger
.forJob(job)
.withSchedule(scheduleBuilder)
.usingJobData(new JobDataMap()) //a HashMap-like structure containing data that only this schedule will have
.startNow() //or startAt(new Date())
.build();
and finally schedule it:
quartzScheduler.scheduleJob(job, trigger);
Lastly, if we want to unschedule ALL jobs (for example in a test environment), we can run:
public void unscheduleJobs() throws SchedulerException {
for (String group : quartzScheduler.getJobGroupNames()) {
for (JobKey jobKey : quartzScheduler.getJobKeys(GroupMatcher.jobGroupEquals(group))) {
final List<Trigger> triggers = (List<Trigger>) quartzScheduler.getTriggersOfJob(jobKey);
final List<TriggerKey> triggerKeys = new ArrayList<>(triggers.size());
for(Trigger trigger : triggers) {
triggerKeys.add(trigger.getKey());
}
quartzScheduler.unscheduleJobs(triggerKeys);
quartzScheduler.deleteJob(jobKey);
}
}
}
No comments:
Post a Comment
With great power comes great responsibility