diff --git a/spring-quartz/pom.xml b/spring-quartz/pom.xml
index 58e72c1d51..4c7ac6eee9 100644
--- a/spring-quartz/pom.xml
+++ b/spring-quartz/pom.xml
@@ -32,6 +32,16 @@
quartz
${quartz.version}
+
+ com.mchange
+ c3p0
+ ${c3p0.version}
+
+
+
+ com.h2database
+ h2
+
@@ -44,7 +54,8 @@
- 2.2.3
+ 2.3.0
+ 0.9.5.2
\ No newline at end of file
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
index 6601df6c94..73416262ce 100644
--- 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
@@ -1,31 +1,32 @@
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.beans.factory.config.PropertiesFactoryBean;
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.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
+import javax.annotation.PostConstruct;
+import java.io.IOException;
+import java.util.Properties;
+
+import static org.quartz.JobBuilder.newJob;
+import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
+import static org.quartz.TriggerBuilder.newTrigger;
+
@Configuration
@ConditionalOnExpression("'${using.spring.schedulerFactory}'=='false'")
public class QrtzScheduler {
- Logger logger = LoggerFactory.getLogger(getClass());
+ private static final Logger logger = LoggerFactory.getLogger(QrtzScheduler.class);
@Autowired
private ApplicationContext applicationContext;
@@ -45,14 +46,9 @@ public class QrtzScheduler {
}
@Bean
- public Scheduler scheduler(Trigger trigger, JobDetail job) throws SchedulerException, IOException {
-
- StdSchedulerFactory factory = new StdSchedulerFactory();
- factory.initialize(new ClassPathResource("quartz.properties").getInputStream());
-
+ public Scheduler scheduler(Trigger trigger, JobDetail job, SchedulerFactoryBean factory) throws SchedulerException {
logger.debug("Getting a handle to the Scheduler");
Scheduler scheduler = factory.getScheduler();
- scheduler.setJobFactory(springBeanJobFactory());
scheduler.scheduleJob(job, trigger);
logger.debug("Starting Scheduler threads");
@@ -60,10 +56,29 @@ public class QrtzScheduler {
return scheduler;
}
+ @Bean
+ public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
+ SchedulerFactoryBean factory = new SchedulerFactoryBean();
+ factory.setJobFactory(springBeanJobFactory());
+ factory.setQuartzProperties(quartzProperties());
+ return factory;
+ }
+
+ public Properties quartzProperties() throws IOException {
+ PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
+ propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
+ propertiesFactoryBean.afterPropertiesSet();
+ return propertiesFactoryBean.getObject();
+ }
+
@Bean
public JobDetail jobDetail() {
- return newJob().ofType(SampleJob.class).storeDurably().withIdentity(JobKey.jobKey("Qrtz_Job_Detail")).withDescription("Invoke Sample Job service...").build();
+ return newJob().ofType(SampleJob.class)
+ .storeDurably()
+ .withIdentity(JobKey.jobKey("Qrtz_Job_Detail"))
+ .withDescription("Invoke Sample Job service...")
+ .build();
}
@Bean
@@ -72,6 +87,10 @@ public class QrtzScheduler {
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();
+ 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
index 7c50f9a231..8bfd856b86 100644
--- 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
@@ -12,7 +12,7 @@ import org.springframework.stereotype.Component;
@Component
public class SampleJob implements Job {
- Logger logger = LoggerFactory.getLogger(getClass());
+ private static final Logger logger = LoggerFactory.getLogger(SampleJob.class);
@Autowired
private SampleJobService jobService;
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
index 9978f61522..40234df1c9 100644
--- 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
@@ -1,7 +1,5 @@
package org.baeldung.springquartz.basics.scheduler;
-import javax.annotation.PostConstruct;
-
import org.baeldung.springquartz.config.AutoWiringSpringBeanJobFactory;
import org.quartz.JobDetail;
import org.quartz.SimpleTrigger;
@@ -9,7 +7,11 @@ import org.quartz.Trigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.quartz.QuartzDataSource;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -19,11 +21,15 @@ import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
+import javax.annotation.PostConstruct;
+import javax.sql.DataSource;
+
@Configuration
+@EnableAutoConfiguration
@ConditionalOnExpression("'${using.spring.schedulerFactory}'=='true'")
public class SpringQrtzScheduler {
- Logger logger = LoggerFactory.getLogger(getClass());
+ private static final Logger logger = LoggerFactory.getLogger(SpringQrtzScheduler.class);
@Autowired
private ApplicationContext applicationContext;
@@ -43,7 +49,9 @@ public class SpringQrtzScheduler {
}
@Bean
- public SchedulerFactoryBean scheduler(Trigger trigger, JobDetail job) {
+ public SchedulerFactoryBean scheduler(Trigger trigger,
+ JobDetail job,
+ DataSource quartzDataSource) {
SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
schedulerFactory.setConfigLocation(new ClassPathResource("quartz.properties"));
@@ -53,6 +61,9 @@ public class SpringQrtzScheduler {
schedulerFactory.setJobDetails(job);
schedulerFactory.setTriggers(trigger);
+ // Comment the following line to use the default Quartz job store.
+ schedulerFactory.setDataSource(quartzDataSource);
+
return schedulerFactory;
}
@@ -81,4 +92,12 @@ public class SpringQrtzScheduler {
trigger.setName("Qrtz_Trigger");
return trigger;
}
+
+ @Bean
+ @QuartzDataSource
+ @ConfigurationProperties(prefix = "spring.datasource")
+ public DataSource quartzDataSource() {
+ return DataSourceBuilder.create().build();
+ }
+
}
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
index ddf4efc2c5..4b976d37b0 100644
--- 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
@@ -4,20 +4,31 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
+import java.util.concurrent.atomic.AtomicInteger;
+
@Service
public class SampleJobService {
- private Logger logger = LoggerFactory.getLogger(getClass());
+ public static final long EXECUTION_TIME = 5000L;
+
+ private static final Logger logger = LoggerFactory.getLogger(SampleJobService.class);
+
+ private AtomicInteger count = new AtomicInteger();
public void executeSampleJob() {
logger.info("The sample job has begun...");
try {
- Thread.sleep(5000);
+ Thread.sleep(EXECUTION_TIME);
} catch (InterruptedException e) {
logger.error("Error while executing sample job", e);
} finally {
+ count.incrementAndGet();
logger.info("Sample job has finished...");
}
}
+
+ public int getNumberOfInvocations() {
+ return count.get();
+ }
}
diff --git a/spring-quartz/src/main/resources/application.properties b/spring-quartz/src/main/resources/application.properties
index 7bdd647e25..557349af2e 100644
--- a/spring-quartz/src/main/resources/application.properties
+++ b/spring-quartz/src/main/resources/application.properties
@@ -1 +1,10 @@
-using.spring.schedulerFactory=true
\ No newline at end of file
+using.spring.schedulerFactory=true
+
+spring.quartz.job-store-type=jdbc
+# Always create the Quartz database on startup
+spring.quartz.jdbc.initialize-schema=always
+
+spring.datasource.jdbc-url=jdbc:h2:mem:spring-quartz;DB_CLOSE_ON_EXIT=FALSE
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=
diff --git a/spring-quartz/src/main/resources/quartz.properties b/spring-quartz/src/main/resources/quartz.properties
index cefaaef8e4..662bb83eb0 100644
--- a/spring-quartz/src/main/resources/quartz.properties
+++ b/spring-quartz/src/main/resources/quartz.properties
@@ -4,7 +4,19 @@ org.quartz.threadPool.threadCount=2
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
# job-store
-org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
+# Enable this property for RAMJobStore
+#org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
-# others
-org.quartz.jobStore.misfireThreshold = 60000
\ No newline at end of file
+# Enable these properties for a JDBCJobStore using JobStoreTX
+org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
+org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+org.quartz.jobStore.dataSource=quartzDataSource
+# Enable this property for JobStoreCMT
+#org.quartz.jobStore.nonManagedTXDataSource=quartzDataSource
+
+# H2 database
+# use an in-memory database & initialise Quartz using their standard SQL script
+org.quartz.dataSource.quartzDataSource.URL=jdbc:h2:mem:spring-quartz;INIT=RUNSCRIPT FROM 'classpath:/org/quartz/impl/jdbcjobstore/tables_h2.sql'
+org.quartz.dataSource.quartzDataSource.driver=org.h2.Driver
+org.quartz.dataSource.quartzDataSource.user=sa
+org.quartz.dataSource.quartzDataSource.password=
diff --git a/spring-quartz/src/test/java/org/baeldung/SpringContextIntegrationTest.java b/spring-quartz/src/test/java/org/baeldung/SpringContextIntegrationTest.java
index 516cc587a7..fec47f045c 100644
--- a/spring-quartz/src/test/java/org/baeldung/SpringContextIntegrationTest.java
+++ b/spring-quartz/src/test/java/org/baeldung/SpringContextIntegrationTest.java
@@ -1,16 +1,30 @@
package org.baeldung;
import org.baeldung.springquartz.SpringQuartzApp;
+import org.baeldung.springquartz.basics.service.SampleJobService;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
+import static org.assertj.core.api.Assertions.assertThat;
+
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringQuartzApp.class)
public class SpringContextIntegrationTest {
+ @Autowired
+ private SampleJobService sampleJobService;
+
@Test
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
+
+ @Test
+ public void whenSchedulerStarts_thenJobsRun() throws InterruptedException {
+ assertThat(sampleJobService.getNumberOfInvocations()).isEqualTo(0);
+ Thread.sleep(SampleJobService.EXECUTION_TIME);
+ assertThat(sampleJobService.getNumberOfInvocations()).isEqualTo(1);
+ }
}