In this solution you will be able to schedule and unschedule pre defined jobs using http requests. In this example we will create a daily, weekly and an oneTime Job. The application is using Quartz
.
<!--Quartz Scheduler -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
First we have to create an AutowiringSpringBeanJobFactory
class extends SpringBeanJobFactory
.
- Subclass of {@link AdaptableJobFactory} that also supports
Spring-style * dependency injection on bean properties. This is
essentially the direct * equivalent of Spring's {@link QuartzJobBean}
in the shape of a Quartz * {@link org.quartz.spi.JobFactory}. * *
Applies scheduler context, job data map and trigger data map
entries * as bean property values. If no matching bean property is
found, the entry * is by default simply ignored. This is analogous to
QuartzJobBean's behavior.
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
beanFactory = applicationContext.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
The second part is to configure the quartz configuration. In this config we need to create a
SchedulerFactoryBean
where we set global config and the application context,
JobDetailFactoryBean
where we set our job, the jobGroup and the class,
CronTriggerFactoryBean
where we set the cron expression.
QuartzConfig.class
@Configuration
public class QuartzConfig {
@Autowired
ApplicationContext context;
@Bean
public SchedulerFactoryBean quartzScheduler(){
SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean();
quartzScheduler.setOverwriteExistingJobs(true);
quartzScheduler.setSchedulerName("job-scheduler");
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(context);
quartzScheduler.setJobFactory(jobFactory);
return quartzScheduler;
}
@Bean
@Scope(value = "prototype")
public JobDetailFactoryBean getJobBean(String jobName, String jobGroup, Class<?> clazz){
JobDetailFactoryBean bean = new JobDetailFactoryBean();
bean.setJobClass(clazz);
bean.setGroup(jobGroup);
bean.setName(jobName);
return bean;
}
@Bean
@Scope(value = "prototype")
public CronTriggerFactoryBean getCronTriggerBean(String cronExpression, String triggerGroup){
CronTriggerFactoryBean bean = new CronTriggerFactoryBean();
bean.setCronExpression(cronExpression);
bean.setGroup(triggerGroup);
return bean;
}
}
So, after the config is done we are now able to create our jobs where the business logic will be placed. For that we have to create a class which implements Job
.
@Component
public class DailyJob implements Job{
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("Daily Job runs!");
}
}
The DailyJob
class is now ready to get scheduled. We want to schedule this job from outside via a http request. In this example we have a controller where we can send the jobname and the cron expression to schedule the dailyJob
.
@Controller
public class JobController {
@Autowired
private Scheduler scheduler;
@Autowired
private ApplicationContext context;;
@ResponseBody
@RequestMapping(value = "/job/create/daily", method = RequestMethod.POST)
public ResponseEntity<JobModel> dailyJob(@RequestBody JobModel jobModel) throws SchedulerException {
JobDetail jobDetail = context.getBean(
JobDetail.class, jobModel.getName(), "MyDailyJob", DailyJob.class);
Trigger cronTrigger = context.getBean(
Trigger.class, jobModel.getCronExpression(), "MyDailyJob");
scheduler.scheduleJob(jobDetail, cronTrigger);
return new ResponseEntity<JobModel>(jobModel, HttpStatus.CREATED);
}
}
What we see here is that we will send a post request with a JobModel
as @RequestBody
. JobModel
is a simple Pojo with two attributes name
and cronExpression
both Strings.
In this method we have to create the bean instances which we have configured previously in our config class. First create JobDetail
with Quartz JobDetail.class
, the name of your job, the name of the group and the Class which should be scheduled (in this case DailyJob.class
). After that we have to create the Trigger with Quartz Trigger.class
, the cronExpression and the group name.
After both beans are created we need to schedule the job now. So we have autowired Quartz Scheduler
to schedule the job. After that the job is enabled and ready to do its job.
So let's test the stuff. Start the application and send a post request to /job/create/daily
:
{"name":"Job 1", "cronExpression":"0 * * * * ?"}
Here we say that the job should run every minute (just to see that everything works). In your console you should see every minute Daily Job runs!
.
And here are some additional things you can do. For example get a list of the scheduled jobs:
@ResponseBody
@RequestMapping("job/list")
public List<String> jobList() throws SchedulerException {
return scheduler.getJobGroupNames();
}
To delete a job you can create endpoints, too. For example:
@ResponseBody
@RequestMapping(value = "job/delete/daily", method = RequestMethod.POST)
public ResponseEntity<Boolean> deleteJob(@RequestBody JobModel jobModel) throws SchedulerException {
JobKey jobKey = new JobKey(jobModel.getName(), "MyDailyJob");
return new ResponseEntity<Boolean>(scheduler.deleteJob(jobKey), HttpStatus.OK);
}
You are free to create many different endpoints to get informations about currently running jobs, how often jobs were running, reschedule jobs and so on. Important is just, that your jobname and the jobgroup( in our case "MyDailyJob"
) are reusable. Those information are needed to create the jobKey.
P.S.: Just to show the other mappings for the other jobs:
@ResponseBody
@RequestMapping(value = "/job/create/weekly", method = RequestMethod.POST)
public ResponseEntity<JobModel> weeklyJob(@RequestBody JobModel jobModel) throws SchedulerException {
JobDetail jobDetail = context.getBean(JobDetail.class, jobModel.getName(), JobGroup.WEEKLY_GROUP.name(),
WeeklyJob.class);
Trigger cronTrigger = context.getBean(Trigger.class, jobModel.getCronExpression(),
JobGroup.WEEKLY_GROUP.name());
scheduler.scheduleJob(jobDetail, cronTrigger);
return new ResponseEntity<JobModel>(jobModel, HttpStatus.CREATED);
}
@ResponseBody
@RequestMapping(value = "/job/create/oneTime", method = RequestMethod.POST)
public ResponseEntity<JobModel> oneTimeJob(@RequestBody JobModel jobModel) throws SchedulerException {
JobDetail jobDetail = context.getBean(JobDetail.class, jobModel.getName(), JobGroup.ONE_TIME_GROUP.name(),
OneTimeJob.class);
Trigger cronTrigger = context.getBean(Trigger.class, jobModel.getCronExpression(),
JobGroup.ONE_TIME_GROUP.name());
scheduler.scheduleJob(jobDetail, cronTrigger);
return new ResponseEntity<JobModel>(jobModel, HttpStatus.CREATED);
}
The full application is on github