How to schedule the Quartz cron jobs on spring boot app startup?
I am integrating Quartz with Spring boot and postgres. I have created all the required tables for quartz.
Issue : The application starts but the Job is not getting executed as per the cron.
I want that the jobs should be automatically scheduled on start-up and should run as per the cron expression.
But right now the application starts and no trigger is fired.
The QuartzJobFactory class
public class QuartzJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
beanFactory = applicationContext.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
The ConfigQuartz class
@Configuration
public class ConfigQuartz {
@Value("${quartz.dataSource.myDS.URL}")
String orgQuartzDatasourceMydsUrl;
@Value("${spring.datasource.username}")
String orgQuartzDatasourceMydsUser;
@Value("${spring.datasource.password}")
String orgQuartzDatasourceMydsPassword;
@Value("${quartz.enabled}")
Boolean isQuartzEnabled;
private static List<Trigger> triggers = new ArrayList<>();
@Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
QuartzJobFactory sampleJobFactory = new QuartzJobFactory();
sampleJobFactory.setApplicationContext(applicationContext);
return sampleJobFactory;
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource, JobFactory jobFactory)
throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setOverwriteExistingJobs(true);
factory.setAutoStartup(isQuartzEnabled);
// factory.setDataSource(dataSource);
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
factory.setTriggers(triggers.toArray(new Trigger[triggers.size()]));
return factory;
}
public Properties setQuartzProperties() {
Properties properties = new Properties();
properties.setProperty("org.quartz.dataSource.myDS.URL",orgQuartzDatasourceMydsUrl);
properties.setProperty("org.quartz.dataSource.myDS.user",orgQuartzDatasourceMydsUser);
properties.setProperty("org.quartz.dataSource.myDS.password",orgQuartzDatasourceMydsPassword);
return properties;
}
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties"));
propertiesFactoryBean.setProperties(setQuartzProperties());
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
public static SimpleTriggerFactoryBean createTrigger(JobDetail jobDetail, long pollFrequencyMs,
String triggerName) {
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail);
factoryBean.setStartDelay(0L);
factoryBean.setRepeatInterval(pollFrequencyMs);
factoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
factoryBean.setName(triggerName);
// in case of misfire, ignore all missed triggers and continue :
factoryBean.setMisfireInstruction(
SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT);
return factoryBean;
}
// Use this method for creating cron triggers instead of simple triggers:
public static CronTriggerFactoryBean createCronTrigger(JobDetail jobDetail, String cronExpression,
String triggerName) throws Exception {
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail);
factoryBean.setCronExpression(cronExpression);
factoryBean.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
factoryBean.setName(triggerName);
try {
factoryBean.afterPropertiesSet();
} catch (ParseException e) {
throw new Exception(e.getMessage());
}
triggers.add(factoryBean.getObject());
return factoryBean;
}
public static JobDetailFactoryBean createJobDetail(Class jobClass) {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(jobClass);
// job has to be durable to be stored in DB:
factoryBean.setDurability(true);
return factoryBean;
}
}
The QuartzJobFactory class
public class QuartzJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
beanFactory = applicationContext.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
The CronExpression class
public class CronExpression {
private static final Map<String, String> cronExpressionMap = ImmutableMap
.<String, String>builder()
.put(TestJob.CLASS_NAME, "* 0 0 ? * * *")
.build();
public static String get(String key) {
return cronExpressionMap.get(key);
}
}
The TestJob class
@Component
@DisallowConcurrentExecution
public class TestJob implements Job {
public static final String CLASS_NAME = "TestJob";
private final String JOB_BEAN_NAME = CLASS_NAME + AppConstants.QUARTZ_JOB_SUFFIX;
private final String TRIGGER_BEAN_NAME = CLASS_NAME + AppConstants.QUARTZ_TRIGGER_SUFFIX;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
LocalDateTime start = LocalDateTime.now();
System.out.println("*************** execute method is running *******");
}
@Bean(name = JOB_BEAN_NAME)
public JobDetailFactoryBean sampleJob() {
return ConfigQuartz.createJobDetail(this.getClass());
}
@Bean(name = TRIGGER_BEAN_NAME)
public CronTriggerFactoryBean sampleJobTrigger(
@Qualifier(JOB_BEAN_NAME) JobDetailFactoryBean jobDetail) throws Exception {
return ConfigQuartz.createCronTrigger(jobDetail.getObject(), CronExpression.get(CLASS_NAME),
CLASS_NAME);
}
}
The main class
EnableConfigurationProperties
@EntityScan(basePackages = {"com.example"})
@ComponentScan({"com.example"})
@SpringBootApplication
@EnableScheduling
public class QuartzSchedulerApplication {
public static void main(String[] args) {
SpringApplication.run(QuartzSchedulerApplication.class, args);
}
}
application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/quartz
spring.datasource.username=postgres
spring.datasource.password=mysecretpassword
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
spring.jpa.show-sql=true
spring.jpa.database=POSTGRESQL
spring.datasource.platform=postgres
spring.datasource.driverClassName=org.postgresql.Driver
spring.jpa.hibernate.ddl-auto=none
spring.datasource.hikari.maximum-pool-size=5
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
quartz.dataSource.myDS.URL=jdbc:postgresql://localhost:5432/quartz
quartz.enabled=true
The quartz.properties
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName = PropScheduler
org.quartz.scheduler.instanceId = 10
#org.quartz.scheduler.instanceId = AUTO
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.dataSource=myDS
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 5000
org.quartz.scheduler.name=test-schedulers
#============================================================================
# Configure Datasources
#============================================================================
org.quartz.dataSource.myDS.driver=org.postgresql.Driver
org.quartz.dataSource.myDS.maxConnections = 5
org.quartz.dataSource.myDS.validationQuery = select 1
#============================================================================
# Configure trigger history loging , enble if needed more information regaring triggers
#============================================================================
#org.quartz.plugin.triggerHistory.class=org.quartz.plugins.history.LoggingTriggerHistoryPlugin
#org.quartz.plugin.triggerHistory.triggerFiredMessage=Trigger [{1}.{0}] fired job [{6}.{5}] scheduled at: {2, date, dd-MM-yyyy HH:mm:ss.SSS}, next scheduled at: {3, date, dd-MM-yyyy HH:mm:ss.SSS}
#org.quartz.plugin.triggerHistory.triggerCompleteMessage=Trigger [{1}.{0}] completed firing job [{6}.{5}] with resulting trigger instruction code: {9}. Next scheduled at: {3, date, dd-MM-yyyy HH:mm:ss.SSS}
#org.quartz.plugin.triggerHistory.triggerMisfiredMessage=Trigger [{1}.{0}] misfired job [{6}.{5}]. Should have fired at: {3, date, dd-MM-yyyy HH:mm:ss.SSS}
Solution 1:
Your TriggerListener class (SimpleTriggerFactoryBean
) doesn't seem to be implementing TriggerListener
interface of quartz.
You can create custom class which implements TriggerListener
and see if that works.
Reference