Issue
I'm setting up a Quartz Scheduler in Spring Boot. I want the scheduled Jobs to be able to inject Beans from the Application Context. For that I created my own Quartz Job Factory:
public final class MyQuartzJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) throws BeansException {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
For keeping a reference to the Scheduler, I have a SchedulerManager Bean that initializes the Scheduler:
public void initScheduler() {
try (InputStream propsInputStream = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("my-quartz.properties")) {
log.info("Creating Quartz scheduler...");
Properties myQuartzProperties = new Properties();
myQuartzProperties.load(propsInputStream);
SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
schedulerFactory.setQuartzProperties(myQuartzProperties);
schedulerFactory.afterPropertiesSet();
schedulerFactory.setJobFactory(quartzJobFactory);
this.scheduler = schedulerFactory.getScheduler();
this.schedulingConfigMap = new HashMap<>();
log.info("Quartz scheduler created.");
} catch (Exception e) {
log.error("Unable to create Quartz scheduler", e);
throw new RuntimeException("Scheduler extension initialization error", e);
}
}
I do the initialization of the Scheduler once the context has been initialized:
public class MyLifecycleListener implements ServletContextListener {
private final SchedulerManager schedulerManager;
@Override
public void contextInitialized(ServletContextEvent sce) {
schedulerManager.initScheduler();
}
}
The quartz configuration file is as follows:
org.quartz.scheduler.instanceName=MyQuartzScheduler
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=12
org.quartz.threadPool.threadPriority=5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
org.quartz.jobStore.misfireThreshold=60000
When starting the application I see my Quartz Scheduler is initialized, but the problem is that I see a second Quartz Scheduler initialized (maybe a Spring default one?).
Mine: 'MyQuartzScheduler' with 12 threads
2022-08-23 14:13:03.239 INFO 24788 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
2022-08-23 14:13:03.240 INFO 24788 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
2022-08-23 14:13:03.241 INFO 24788 --- [ main] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.3.2) 'MyQuartzScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 12 threads.
Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
2022-08-23 14:13:03.241 INFO 24788 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'MyQuartzScheduler' initialized from an externally provided properties instance.
Some milliseconds later, another one called 'quartzScheduler' with 10 threads.
2022-08-23 14:13:03.713 INFO 24788 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
2022-08-23 14:13:03.713 INFO 24788 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
2022-08-23 14:13:03.713 INFO 24788 --- [ main] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.3.2) 'quartzScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
2022-08-23 14:13:03.713 INFO 24788 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'quartzScheduler' initialized from an externally provided properties instance.
How can I disable this second one?
Sample project can be found here: https://github.com/dajoropo/spring-quartz-demo/tree/stackoverflow_question_73459083
Solution
The easiest way is to use the Spring configured scheduler instead of doing it yourself. Move the quartz.properties
to the spring.quartz
namespace in the application.properties
.
spring.quartz.scheduler-name=MyQuartzScheduler
spring.quartz.properties.org.quartz.threadPool.threadCount=12
spring.quartz.properties.org.quartz.threadPool.threadPriority=5
spring.quartz.properties.org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
spring.quartz.properties.org.quartz.jobStore.misfireThreshold=60000
To configure your own JobFactory
write a SchedulerFactoryBeanCustomizer
to configure it on the SchedulerFactoryBean
.
@Component
class MySchedulerFactoryBeanCustomizer implements SchedulerFactoryBeanCustomizer {
void customize(SchedulerFactoryBean schedulerFactoryBean) {
schedulerFactoryBean.setJobFactory(new MyQuartzJobFactory());
}
}
Now ditch the quartz.properties
, MyLifecycleListener
and the SchedulerManager
and let Spring handle all that for you.
Answered By - M. Deinum
Answer Checked By - Marie Seifert (JavaFixing Admin)