diff --git a/spring-quartz/.gitignore b/spring-quartz/.gitignore new file mode 100644 index 0000000000..2e9f1c1fe0 --- /dev/null +++ b/spring-quartz/.gitignore @@ -0,0 +1,3 @@ +*.iml +.idea +target/ diff --git a/spring-quartz/README.md b/spring-quartz/README.md new file mode 100644 index 0000000000..caac75e5d4 --- /dev/null +++ b/spring-quartz/README.md @@ -0,0 +1,22 @@ +========================================================================= + +## Scheduling in Spring with Quartz Example Project +This is the first example where we configure a basic scheduler. +##### Spring boot application, Main class +### +``` +org.baeldung.springquartz.SpringQuartzApp +``` +###### + +##### Configuration in *application.properties* +#### + + - Default: configures scheduler using Spring convenience classes: + ``` + using.spring.schedulerFactory=true + ``` + - To configure scheduler using Quartz API: + ``` + using.spring.schedulerFactory=false + ``` \ No newline at end of file diff --git a/spring-quartz/pom.xml b/spring-quartz/pom.xml new file mode 100644 index 0000000000..ce286f3d1f --- /dev/null +++ b/spring-quartz/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + + org.baeldung + spring-quartz + spring-quartz + 0.0.1-SNAPSHOT + jar + Demo project for Scheduling in Spring with Quartz + + + org.springframework.boot + spring-boot-starter-parent + 1.2.6.RELEASE + + + + + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-jdbc + + + + + org.springframework + spring-context-support + + + + + org.quartz-scheduler + quartz + 2.2.1 + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + + src/main/resources + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/spring-quartz/src/main/java/org/baeldung/springquartz/SpringQuartzApp.java b/spring-quartz/src/main/java/org/baeldung/springquartz/SpringQuartzApp.java new file mode 100644 index 0000000000..e51e4ad43d --- /dev/null +++ b/spring-quartz/src/main/java/org/baeldung/springquartz/SpringQuartzApp.java @@ -0,0 +1,16 @@ +package org.baeldung.springquartz; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableScheduling; + +@ComponentScan +@EnableScheduling +public class SpringQuartzApp { + + public static void main(String[] args) { + new SpringApplicationBuilder(SpringQuartzApp.class) + .showBanner(false).run(args); + } +} diff --git a/spring-quartz/src/main/java/org/baeldung/springquartz/basics/scheduler/QrtzScheduler.java b/spring-quartz/src/main/java/org/baeldung/springquartz/basics/scheduler/QrtzScheduler.java new file mode 100644 index 0000000000..a944f8fe43 --- /dev/null +++ b/spring-quartz/src/main/java/org/baeldung/springquartz/basics/scheduler/QrtzScheduler.java @@ -0,0 +1,84 @@ +package org.baeldung.springquartz.basics.scheduler; + +import static org.quartz.JobBuilder.newJob; +import static org.quartz.SimpleScheduleBuilder.simpleSchedule; +import static org.quartz.TriggerBuilder.newTrigger; + +import java.io.IOException; + +import javax.annotation.PostConstruct; + +import org.baeldung.springquartz.config.AutoWiringSpringBeanJobFactory; +import org.quartz.*; +import org.quartz.impl.StdSchedulerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.scheduling.quartz.SpringBeanJobFactory; + +@Configuration +@ConditionalOnExpression("'${using.spring.schedulerFactory}'=='false'") +public class QrtzScheduler { + + Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private ApplicationContext applicationContext; + + @PostConstruct + public void init() { + logger.info("Hello world from Quartz..."); + } + + @Bean + public SpringBeanJobFactory springBeanJobFactory() { + AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory(); + logger.debug("Configuring Job factory"); + + jobFactory.setApplicationContext(applicationContext); + return jobFactory; + } + + @Bean + public Scheduler scheduler(Trigger trigger, JobDetail job) + throws SchedulerException, IOException { + + StdSchedulerFactory factory = new StdSchedulerFactory(); + factory.initialize(new ClassPathResource("quartz.properties").getInputStream()); + + logger.debug("Getting a handle to the Scheduler"); + Scheduler scheduler = factory.getScheduler(); + scheduler.setJobFactory(springBeanJobFactory()); + scheduler.scheduleJob(job, trigger); + + logger.debug("Starting Scheduler threads"); + scheduler.start(); + return scheduler; + } + + @Bean + public JobDetail jobDetail() { + + return newJob().ofType(SampleJob.class).storeDurably() + .withIdentity(JobKey.jobKey("Qrtz_Job_Detail")) + .withDescription("Invoke Sample Job service...").build(); + } + + @Bean + public Trigger trigger(JobDetail job) { + + int frequencyInSec = 10; + logger.info("Configuring trigger to fire every {} seconds", frequencyInSec); + + return newTrigger().forJob(job).withIdentity(TriggerKey.triggerKey("Qrtz_Trigger")) + .withDescription("Sample trigger") + .withSchedule( + simpleSchedule().withIntervalInSeconds(frequencyInSec).repeatForever()) + .build(); + } +} diff --git a/spring-quartz/src/main/java/org/baeldung/springquartz/basics/scheduler/SampleJob.java b/spring-quartz/src/main/java/org/baeldung/springquartz/basics/scheduler/SampleJob.java new file mode 100644 index 0000000000..9474272a3c --- /dev/null +++ b/spring-quartz/src/main/java/org/baeldung/springquartz/basics/scheduler/SampleJob.java @@ -0,0 +1,29 @@ +package org.baeldung.springquartz.basics.scheduler; + +import org.baeldung.springquartz.basics.service.SampleJobService; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SampleJob implements Job { + + Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private SampleJobService jobService; + + public void execute(JobExecutionContext context) throws JobExecutionException { + + logger.info("Job ** {} ** fired @ {}", context.getJobDetail().getKey().getName(), + context.getFireTime()); + + jobService.executeSampleJob(); + + logger.info("Next job scheduled @ {}", context.getNextFireTime()); + } +} diff --git a/spring-quartz/src/main/java/org/baeldung/springquartz/basics/scheduler/SpringQrtzScheduler.java b/spring-quartz/src/main/java/org/baeldung/springquartz/basics/scheduler/SpringQrtzScheduler.java new file mode 100644 index 0000000000..9978f61522 --- /dev/null +++ b/spring-quartz/src/main/java/org/baeldung/springquartz/basics/scheduler/SpringQrtzScheduler.java @@ -0,0 +1,84 @@ +package org.baeldung.springquartz.basics.scheduler; + +import javax.annotation.PostConstruct; + +import org.baeldung.springquartz.config.AutoWiringSpringBeanJobFactory; +import org.quartz.JobDetail; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.scheduling.quartz.JobDetailFactoryBean; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean; +import org.springframework.scheduling.quartz.SpringBeanJobFactory; + +@Configuration +@ConditionalOnExpression("'${using.spring.schedulerFactory}'=='true'") +public class SpringQrtzScheduler { + + Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private ApplicationContext applicationContext; + + @PostConstruct + public void init() { + logger.info("Hello world from Spring..."); + } + + @Bean + public SpringBeanJobFactory springBeanJobFactory() { + AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory(); + logger.debug("Configuring Job factory"); + + jobFactory.setApplicationContext(applicationContext); + return jobFactory; + } + + @Bean + public SchedulerFactoryBean scheduler(Trigger trigger, JobDetail job) { + + SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean(); + schedulerFactory.setConfigLocation(new ClassPathResource("quartz.properties")); + + logger.debug("Setting the Scheduler up"); + schedulerFactory.setJobFactory(springBeanJobFactory()); + schedulerFactory.setJobDetails(job); + schedulerFactory.setTriggers(trigger); + + return schedulerFactory; + } + + @Bean + public JobDetailFactoryBean jobDetail() { + + JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean(); + jobDetailFactory.setJobClass(SampleJob.class); + jobDetailFactory.setName("Qrtz_Job_Detail"); + jobDetailFactory.setDescription("Invoke Sample Job service..."); + jobDetailFactory.setDurability(true); + return jobDetailFactory; + } + + @Bean + public SimpleTriggerFactoryBean trigger(JobDetail job) { + + SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); + trigger.setJobDetail(job); + + int frequencyInSec = 10; + logger.info("Configuring trigger to fire every {} seconds", frequencyInSec); + + trigger.setRepeatInterval(frequencyInSec * 1000); + trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY); + trigger.setName("Qrtz_Trigger"); + return trigger; + } +} diff --git a/spring-quartz/src/main/java/org/baeldung/springquartz/basics/service/SampleJobService.java b/spring-quartz/src/main/java/org/baeldung/springquartz/basics/service/SampleJobService.java new file mode 100644 index 0000000000..ddf4efc2c5 --- /dev/null +++ b/spring-quartz/src/main/java/org/baeldung/springquartz/basics/service/SampleJobService.java @@ -0,0 +1,23 @@ +package org.baeldung.springquartz.basics.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class SampleJobService { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + public void executeSampleJob() { + + logger.info("The sample job has begun..."); + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + logger.error("Error while executing sample job", e); + } finally { + logger.info("Sample job has finished..."); + } + } +} diff --git a/spring-quartz/src/main/java/org/baeldung/springquartz/config/AutoWiringSpringBeanJobFactory.java b/spring-quartz/src/main/java/org/baeldung/springquartz/config/AutoWiringSpringBeanJobFactory.java new file mode 100644 index 0000000000..0e24238467 --- /dev/null +++ b/spring-quartz/src/main/java/org/baeldung/springquartz/config/AutoWiringSpringBeanJobFactory.java @@ -0,0 +1,35 @@ +package org.baeldung.springquartz.config; + +import org.quartz.spi.TriggerFiredBundle; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.scheduling.quartz.SpringBeanJobFactory; + +/** + * Adds auto-wiring support to quartz jobs. + * @see "https://gist.github.com/jelies/5085593" + */ +public final class AutoWiringSpringBeanJobFactory extends SpringBeanJobFactory + implements ApplicationContextAware { + + private transient AutowireCapableBeanFactory beanFactory; + + + 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; + } + +} diff --git a/spring-quartz/src/main/resources/application.properties b/spring-quartz/src/main/resources/application.properties new file mode 100644 index 0000000000..7bdd647e25 --- /dev/null +++ b/spring-quartz/src/main/resources/application.properties @@ -0,0 +1 @@ +using.spring.schedulerFactory=true \ No newline at end of file diff --git a/spring-quartz/src/main/resources/logback.xml b/spring-quartz/src/main/resources/logback.xml new file mode 100644 index 0000000000..be0937fefe --- /dev/null +++ b/spring-quartz/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + %date [%thread] %-5level %logger{36} - %message%n + + + + + + + + + + + \ No newline at end of file diff --git a/spring-quartz/src/main/resources/quartz.properties b/spring-quartz/src/main/resources/quartz.properties new file mode 100644 index 0000000000..cefaaef8e4 --- /dev/null +++ b/spring-quartz/src/main/resources/quartz.properties @@ -0,0 +1,10 @@ +# thread-pool +org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool +org.quartz.threadPool.threadCount=2 +org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true + +# job-store +org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore + +# others +org.quartz.jobStore.misfireThreshold = 60000 \ No newline at end of file