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..db91ced3f4 --- /dev/null +++ b/spring-quartz/README.md @@ -0,0 +1,4 @@ +========= + +## Scheduling in Spring with Quartz Example Project + diff --git a/spring-quartz/pom.xml b/spring-quartz/pom.xml new file mode 100644 index 0000000000..461d5494e9 --- /dev/null +++ b/spring-quartz/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + org.baeldung + spring-quartz + 0.0.1-SNAPSHOT + + spring-quartz-basics + + pom + + spring-quartz + Demo project for Scheduling in Spring with Quartz + + + org.springframework.boot + spring-boot-starter-parent + 1.1.12.RELEASE + + + + + UTF-8 + 1.7 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-quartz/spring-quartz-basics/.gitignore b/spring-quartz/spring-quartz-basics/.gitignore new file mode 100644 index 0000000000..0bdd78e13a --- /dev/null +++ b/spring-quartz/spring-quartz-basics/.gitignore @@ -0,0 +1,2 @@ +target/ +*.iml \ No newline at end of file diff --git a/spring-quartz/spring-quartz-basics/README.md b/spring-quartz/spring-quartz-basics/README.md new file mode 100644 index 0000000000..735f26fca1 --- /dev/null +++ b/spring-quartz/spring-quartz-basics/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 +### +``` +SpringQuartzBasicsApp +``` +###### + +##### 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/spring-quartz-basics/pom.xml b/spring-quartz/spring-quartz-basics/pom.xml new file mode 100644 index 0000000000..12d45625da --- /dev/null +++ b/spring-quartz/spring-quartz-basics/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + org.baeldung + spring-quartz-basics + 0.0.1-SNAPSHOT + pom + + spring-quartz-basics + Demo project for Scheduling in Spring with Quartz + + + org.springframework.boot + spring-boot-starter-parent + 1.1.12.RELEASE + + + + + UTF-8 + 1.7 + + + + + + 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 + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/SpringQuartzBasicsApp.java b/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/SpringQuartzBasicsApp.java new file mode 100644 index 0000000000..8069edb370 --- /dev/null +++ b/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/SpringQuartzBasicsApp.java @@ -0,0 +1,16 @@ +package org.baeldung.springquartz; + +import org.springframework.boot.SpringApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; + +@Configuration +@ComponentScan +@EnableScheduling +public class SpringQuartzBasicsApp { + + public static void main(String[] args) { + SpringApplication.run(SpringQuartzBasicsApp.class, args); + } +} diff --git a/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/config/AutoWiringSpringBeanJobFactory.java b/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/config/AutoWiringSpringBeanJobFactory.java new file mode 100644 index 0000000000..0e24238467 --- /dev/null +++ b/spring-quartz/spring-quartz-basics/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/spring-quartz-basics/src/main/java/org/baeldung/springquartz/scheduler/QrtzScheduler.java b/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/scheduler/QrtzScheduler.java new file mode 100644 index 0000000000..5ea7e330f6 --- /dev/null +++ b/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/scheduler/QrtzScheduler.java @@ -0,0 +1,86 @@ +package org.baeldung.springquartz.scheduler; + +import static org.quartz.JobBuilder.newJob; +import static org.quartz.SimpleScheduleBuilder.simpleSchedule; +import static org.quartz.TriggerBuilder.newTrigger; + +import org.baeldung.springquartz.config.AutoWiringSpringBeanJobFactory; +import org.quartz.JobDetail; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.impl.StdSchedulerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +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; + +import javax.annotation.PostConstruct; +import java.io.IOException; + +@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(name = "SpringJobFactory") + public SpringBeanJobFactory springBeanJobFactory() { + AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory(); + _logger.debug("Configuring Job factory"); + + jobFactory.setApplicationContext(applicationContext); + return jobFactory; + } + + @Bean(name = "Qrtz_Scheduler") + public Scheduler scheduler(@Qualifier("Qrtz_Trigger") Trigger trigger, + @Qualifier("Qrtz_Job_Detail") 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(name = "Qrtz_Job_Detail") + public JobDetail jobDetail() { + + return newJob().ofType(SampleJob.class).storeDurably().withIdentity("Qrtz_Job_Detail") + .withDescription("Invoke Sample Job service...").build(); + } + + @Bean(name = "Qrtz_Trigger") + public Trigger trigger(@Qualifier("Qrtz_Job_Detail") JobDetail job) { + + int frequencyInSec = 10; + _logger.info("Configuring trigger to fire every {} seconds", frequencyInSec); + + return newTrigger().forJob(job).withIdentity("Qrtz_Trigger") + .withDescription("Sample trigger") + .withSchedule( + simpleSchedule().withIntervalInSeconds(frequencyInSec).repeatForever()) + .build(); + } +} diff --git a/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/scheduler/SampleJob.java b/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/scheduler/SampleJob.java new file mode 100644 index 0000000000..e408f6f640 --- /dev/null +++ b/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/scheduler/SampleJob.java @@ -0,0 +1,30 @@ +package org.baeldung.springquartz.scheduler; + +import org.baeldung.springquartz.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.context.annotation.Scope; +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/spring-quartz-basics/src/main/java/org/baeldung/springquartz/scheduler/SpringQrtzScheduler.java b/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/scheduler/SpringQrtzScheduler.java new file mode 100644 index 0000000000..7c54748150 --- /dev/null +++ b/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/scheduler/SpringQrtzScheduler.java @@ -0,0 +1,85 @@ +package org.baeldung.springquartz.scheduler; + +import org.baeldung.springquartz.config.AutoWiringSpringBeanJobFactory; +import org.quartz.JobDetail; +import org.quartz.SchedulerException; +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.beans.factory.annotation.Qualifier; +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; + +import javax.annotation.PostConstruct; + +@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(name = "SpringJobFactory") + public SpringBeanJobFactory springBeanJobFactory() { + AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory(); + _logger.debug("Configuring Job factory"); + + jobFactory.setApplicationContext(applicationContext); + return jobFactory; + } + + @Bean(name = "Spring_Scheduler") + public SchedulerFactoryBean scheduler(@Qualifier("Spring_Trigger") Trigger trigger, + @Qualifier("Spring_Job_Detail") 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(name = "Spring_Job_Detail") + public JobDetailFactoryBean jobDetail() { + + JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean(); + jobDetailFactory.setJobClass(SampleJob.class); + jobDetailFactory.setDescription("Invoke Sample Job service..."); + jobDetailFactory.setDurability(true); + return jobDetailFactory; + } + + @Bean(name = "Spring_Trigger") + public SimpleTriggerFactoryBean trigger(@Qualifier("Spring_Job_Detail") 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); + return trigger; + } +} diff --git a/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/service/SampleJobService.java b/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/service/SampleJobService.java new file mode 100644 index 0000000000..708757aad0 --- /dev/null +++ b/spring-quartz/spring-quartz-basics/src/main/java/org/baeldung/springquartz/service/SampleJobService.java @@ -0,0 +1,24 @@ +package org.baeldung.springquartz.service; + +import org.quartz.JobExecutionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class SampleJobService { + + private Logger _log = LoggerFactory.getLogger(getClass()); + + public void executeSampleJob() { + + _log.info("The sample job has begun..."); + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + _log.error("Error while executing sample job", e); + } finally { + _log.info("Sample job has finished..."); + } + } +} diff --git a/spring-quartz/spring-quartz-basics/src/main/resources/application.properties b/spring-quartz/spring-quartz-basics/src/main/resources/application.properties new file mode 100644 index 0000000000..7bdd647e25 --- /dev/null +++ b/spring-quartz/spring-quartz-basics/src/main/resources/application.properties @@ -0,0 +1 @@ +using.spring.schedulerFactory=true \ No newline at end of file diff --git a/spring-quartz/spring-quartz-basics/src/main/resources/logback.xml b/spring-quartz/spring-quartz-basics/src/main/resources/logback.xml new file mode 100644 index 0000000000..be0937fefe --- /dev/null +++ b/spring-quartz/spring-quartz-basics/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/spring-quartz-basics/src/main/resources/quartz.properties b/spring-quartz/spring-quartz-basics/src/main/resources/quartz.properties new file mode 100644 index 0000000000..cefaaef8e4 --- /dev/null +++ b/spring-quartz/spring-quartz-basics/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 diff --git a/spring-quartz/spring-quartz-basics/src/test/java/org/baeldung/springquartz/SpringQuartzApplicationTests.java b/spring-quartz/spring-quartz-basics/src/test/java/org/baeldung/springquartz/SpringQuartzApplicationTests.java new file mode 100644 index 0000000000..c4bc4169a3 --- /dev/null +++ b/spring-quartz/spring-quartz-basics/src/test/java/org/baeldung/springquartz/SpringQuartzApplicationTests.java @@ -0,0 +1,13 @@ +package org.baeldung.springquartz; + + +//@RunWith(SpringJUnit4ClassRunner.class) +//@SpringApplicationConfiguration(classes = SpringQuartzBasicsApp.class) +public class SpringQuartzApplicationTests { + +// @Test + public final void whenContextIsBootstrapped_thenNoExceptions() { + System.out.println("Context bootstrapped..."); + } + +}