We have a Spring Boot application upgrading to version 2.6.6 from 1.5.9.RELEASE. The application uses the quartz scheduler with db persistence using the autoconfigured datasource. After upgrading to 2.6.6, is seems that the datasource is no longer injected(?) when quartz is initialized and fails when attempting to initialize the SchedulerFactoryBean

This similar to 2.5.7 upgrade, issue with quartz datasource #28846

Exception in 2.6.6

Invocation of init method failed; nested exception is org.quartz.SchedulerConfigException: DataSource name not set. 

java.sql.SQLException: There is no DataSource named 'null'
    at org.quartz.utils.DBConnectionManager.shutdown(DBConnectionManager.java:135)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.shutdown(JobStoreSupport.java:741)
    at org.quartz.core.QuartzScheduler.shutdown(QuartzScheduler.java:760)
    at org.quartz.impl.StdSchedulerFactory.shutdownFromInstantiateException(StdSchedulerFactory.java:1372)
    at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:1356)
    at org.quartz.impl.StdSchedulerFactory.getScheduler(StdSchedulerFactory.java:1519)
    at org.springframework.scheduling.quartz.SchedulerFactoryBean.createScheduler(SchedulerFactoryBean.java:679)
    at org.springframework.scheduling.quartz.SchedulerFactoryBean.prepareScheduler(SchedulerFactoryBean.java:616)
    at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:504)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1389)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1309)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:656)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:639)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:953)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:740)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:415)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301)

Comment From: scottfrederick

@mtsmel Please see this comment on a related issue. Are you setting org.quartz.jobstore.class in your configuration as in the example at the top of that issue, and if so does removing that setting fix your issue?

Comment From: mtsmel

Thanks Scott...

No we are not setting the jobstore.class in the yml file..

quartz: enabled: true #This CRON means run every day at 3 am toaNightlyCron: 0 0 3 * * ? pastDueCriticalActivitiesCron: 0 0 0 1 1 ? 2100

We do have this "customization" in our QuartzConfig class:

@Autowired
private DataSource dataSource;

@Bean
public SchedulerFactoryBean quartzScheduler() {
    SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean();
    quartzScheduler.setDataSource(this.dataSource);
    quartzScheduler.setTransactionManager(transactionManager);
    quartzScheduler.setOverwriteExistingJobs(true);
    quartzScheduler.setSchedulerName("pete-quartz-scheduler");

    // custom job factory of spring with DI support for @Autowired!
    AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
    jobFactory.setApplicationContext(applicationContext);
    quartzScheduler.setJobFactory(jobFactory);

    quartzScheduler.setQuartzProperties(quartzProperties());

Should we remove the lines above from the QuartzConfig class?

Comment From: mtsmel

commenting out these lines did not fix the error:

AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); jobFactory.setApplicationContext(applicationContext); quartzScheduler.setJobFactory(jobFactory);

Comment From: scottfrederick

I would remove the entire quartzScheduler() configuration method and replace it with properties used by Spring Boot's auto-configuration (see the docs and code). If you need to further customize the SchedulerFactoryBean created by Boot auto-configuration, you can create a SchedulerFactoryBeanCustomizer.

Comment From: mtsmel

ok thx.. will give that a try...

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

Comment From: ghost

So the workaround is to include in the yaml?

Spring:
  quartz:
    properties.org.quartz:
      dataSource:
        XXXX:

What if I don't want to include datasource 2 times in my yaml? Not very good for maintenance.

Comment From: wilkinsona

@fecogc It's impossible to say without knowing what that problem is that you're working around. If you use Spring Boot's auto-configuration with the JDBC job store, Quartz will be configured to use the context's DataSource without you doing anything. If you want to use a Quartz-specific Datasource, you can define one and annotate its @Bean method with @QuartzDataSource as described in the documentation.

Comment From: ghost

@wilkinsona I fixed the issue, you can close the issue. Thanks

I removed the customized datasource. And added into quartz.properties org.quartz.jobStore.class=org.springframework.scheduling.quartz.LocalDataSourceJobStore

Comment From: HomeOfTheWizard

Thanks @wilkinsona and @fecogc. I know the ticket is closed, but since I ended up here looking for a solution, and that it wasn't clear what to do, I will provide my solution for the others still looking for one.

https://stackoverflow.com/questions/56167468/how-to-set-datasource-in-quartz-schedular-error-org-quartz-schedulerexception/74378516#74378516

Comment From: deantray

FYI a critical change was made in SchedulerFactoryBean.java from this code

        mergedProps.setProperty(StdSchedulerFactory.PROP_JOB_STORE_CLASS, LocalDataSourceJobStore.class.getName());

to this code ->

        mergedProps.putIfAbsent(StdSchedulerFactory.PROP_JOB_STORE_CLASS, LocalDataSourceJobStore.class.getName());

which broke my upgrade as the new one started using "org.quartz.jobStore.class -> org.quartz.impl.jdbcjobstore.JobStoreTX" without us knowing and that completely did not work and would fail on dsName== null check :( where the other job does some init stuff that sets the dsName that JobStoreTX is missing.

Comment From: snicoll

@deantray please follow the link described in this issue that already explains all of this.