JCN
JCN

Reputation: 539

Spring-Boot with Quartz and multiple schedulers

I am working with a scenario where we have one database with multiple schemas, one schema for each customer. This allows each customer to set different schedules for their jobs. All schemas have the same set of jobs, only the schedules differ.

I need to write one Spring-Boot app to run all jobs from all schemas.

It seems like this would be done by defining different quartz.properties for each schema, and then configuring a different Scheduler for each one, like this:

@SpringBootApplication
@Configuration
public class MyApplication{

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

    @Bean
    public Scheduler schedulerA(Trigger trigger, JobDetail job) {
        StdSchedulerFactory factory = new StdSchedulerFactory();
        factory.initialize(new ClassPathResource("quartzA.properties").getInputStream());

        Scheduler scheduler = factory.getScheduler();
        scheduler.setJobFactory(springBeanJobFactory());
        scheduler.scheduleJob(job, trigger);

        scheduler.start();
        return scheduler;
    }

    @Bean
    public Scheduler schedulerB(Trigger trigger, JobDetail job) {
        StdSchedulerFactory factory = new StdSchedulerFactory();
        factory.initialize(new ClassPathResource("quartzB.properties").getInputStream());

        Scheduler scheduler = factory.getScheduler();
        scheduler.setJobFactory(springBeanJobFactory());
        scheduler.scheduleJob(job, trigger);

        scheduler.start();
        return scheduler;
    }    
}

My question is, is this correct? Can I just define these schedulers in my SpringBootApplication class annotated with @Configuration, and expect it to work (assuming the properties are correct)? Am I missing anything?

Upvotes: 4

Views: 7017

Answers (2)

dpolivaev
dpolivaev

Reputation: 558

Inspired by the above example I found a way to use configurations managed in application properties which seems to be easier and more consistent with the rest of the Spring-Boot app. It is particularly useful to reuse the data source configuration. Any number of beans of the second kind is possible.

@Configuration
class MainQuartzConfiguration {
    /**
     * Main scheduler bean where all jobDetails, calendars and trigger beans are attached.
     * 
     */
    @Primary @Bean
    public SchedulerFactoryBean mainScheduler(QuartzProperties properties,
            ObjectProvider<SchedulerFactoryBeanCustomizer> customizers,
            ObjectProvider<JobDetail[]> jobDetails, Map<String, Calendar> calendars,
            ObjectProvider<Trigger[]> triggers, ApplicationContext applicationContext) {
        SchedulerFactoryBean factory = new QuartzAutoConfiguration(properties, customizers, jobDetails, calendars, triggers, applicationContext)
                .quartzScheduler();
        factory.setSchedulerName("mainScheduler");
        return factory;
    }
}

@Configuration 
class AnotherConfiguration {
    /**
     * Second scheduler bean which has the same configuration but different thread count and thread priority.
     */
    
    @Bean
    SchedulerFactoryBean secondScheduler(
            QuartzProperties properties,
            ObjectProvider<SchedulerFactoryBeanCustomizer> customizers,
            @Value("${spring.quartz.properties.secondScheduler.org.quartz.threadPool.threadPriority:7}") int threadPriority,
            @Value("${spring.quartz.properties.secondScheduler.org.quartz.threadPool.threadCount:1}") int threadCount,
            ApplicationContext applicationContext)
    {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        SpringBeanJobFactory jobFactory = new SpringBeanJobFactory();
        jobFactory.setApplicationContext(applicationContext);
        schedulerFactoryBean.setJobFactory(jobFactory);
        schedulerFactoryBean.setSchedulerName("secondScheduler");
        schedulerFactoryBean.setAutoStartup(properties.isAutoStartup());
        schedulerFactoryBean
        .setStartupDelay((int) properties.getStartupDelay().getSeconds());
        schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(
                properties.isWaitForJobsToCompleteOnShutdown());
        Properties propertiesVariant = new Properties();
        propertiesVariant.putAll(properties.getProperties());
        propertiesVariant.setProperty("org.quartz.threadPool.threadPriority", Integer.toString(threadPriority));
        propertiesVariant.setProperty("org.quartz.threadPool.threadCount", Integer.toString(threadCount));
        schedulerFactoryBean.setQuartzProperties(propertiesVariant);
        schedulerFactoryBean.setJobDetails(CatalogBenchmarkJob.createJob());
        customizers.orderedStream().forEach(
                (customizer) -> customizer.customize(schedulerFactoryBean));
        return schedulerFactoryBean;
    }
}

Upvotes: 1

Amit Phaltankar
Amit Phaltankar

Reputation: 3424

My question is, is this correct? Can I just define these schedulers in my SpringBootApplication class annotated with @Configuration

This is correct. Alternatively you can use Spring @Schelduled annotation with a Cron defined in properties files.

@Scheduled(cron = "0 15 10 15 * ?")
public void scheduleTaskUsingCronExpression() {
.
.
.

But, if you want more control over the jobs like failover, retry policy or track and run/rerun jobs from a dashboard. Think of spring-batch

Upvotes: 2

Related Questions