Starter for using the Quartz scheduler in Spring Boot applications with auto-configuration support
—
Interfaces and annotations for customizing scheduler behavior, data sources, and transaction managers. Enables fine-grained control over Quartz configuration beyond what's available through standard properties.
Functional interface that allows programmatic customization of the SchedulerFactoryBean before it's fully initialized.
/**
* Callback interface for customizing SchedulerFactoryBean
* Implement this interface to apply custom configuration that cannot be achieved through properties
*/
@FunctionalInterface
public interface SchedulerFactoryBeanCustomizer {
/**
* Customize the SchedulerFactoryBean before initialization
* @param schedulerFactoryBean The scheduler factory bean to customize
*/
void customize(SchedulerFactoryBean schedulerFactoryBean);
}Usage Examples:
@Configuration
public class QuartzCustomizerConfiguration {
@Bean
public SchedulerFactoryBeanCustomizer schedulerCustomizer() {
return schedulerFactoryBean -> {
// Set custom scheduler context data
Map<String, Object> schedulerContextMap = new HashMap<>();
schedulerContextMap.put("applicationName", "MyApplication");
schedulerContextMap.put("environment", "production");
schedulerFactoryBean.setSchedulerContextAsMap(schedulerContextMap);
// Set custom configuration location
schedulerFactoryBean.setConfigLocation(new ClassPathResource("quartz-custom.properties"));
// Configure application context integration
schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContext");
// Set custom scheduler listeners
schedulerFactoryBean.setGlobalJobListeners(new CustomJobListener());
schedulerFactoryBean.setGlobalTriggerListeners(new CustomTriggerListener());
};
}
@Bean
public SchedulerFactoryBeanCustomizer dataSourceCustomizer(
@Qualifier("quartzDataSource") DataSource quartzDataSource) {
return schedulerFactoryBean -> {
schedulerFactoryBean.setDataSource(quartzDataSource);
schedulerFactoryBean.setNonTransactionalDataSource(quartzDataSource);
};
}
@Bean
public SchedulerFactoryBeanCustomizer threadPoolCustomizer() {
return schedulerFactoryBean -> {
Properties quartzProperties = new Properties();
quartzProperties.setProperty("org.quartz.threadPool.threadCount", "20");
quartzProperties.setProperty("org.quartz.threadPool.threadPriority", "5");
quartzProperties.setProperty("org.quartz.threadPool.class",
"org.quartz.simpl.SimpleThreadPool");
schedulerFactoryBean.setQuartzProperties(quartzProperties);
};
}
}Qualifier annotation for specifying a dedicated DataSource for Quartz operations, separate from the application's primary DataSource.
/**
* Qualifier annotation for DataSource to be injected into Quartz auto-configuration
* Used when you want to use a separate database for Quartz job storage
* Can be applied to secondary DataSource when another is marked as @Primary
*/
@Target({
ElementType.FIELD,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.TYPE,
ElementType.ANNOTATION_TYPE
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier
public @interface QuartzDataSource {
}Usage Examples:
@Configuration
public class DataSourceConfiguration {
// Primary DataSource for application data
@Bean
@Primary
@ConfigurationProperties("spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
// Dedicated DataSource for Quartz
@Bean
@QuartzDataSource
@ConfigurationProperties("spring.datasource.quartz")
public DataSource quartzDataSource() {
return DataSourceBuilder.create().build();
}
// Using QuartzDataSource in custom components
@Service
public class QuartzManagementService {
private final JdbcTemplate quartzJdbcTemplate;
public QuartzManagementService(@QuartzDataSource DataSource quartzDataSource) {
this.quartzJdbcTemplate = new JdbcTemplate(quartzDataSource);
}
public List<String> getJobNames() {
return quartzJdbcTemplate.queryForList(
"SELECT JOB_NAME FROM QRTZ_JOB_DETAILS", String.class);
}
}
}# Primary database configuration
spring.datasource.url=jdbc:postgresql://localhost:5432/myapp
spring.datasource.username=app_user
spring.datasource.password=app_password
# Quartz database configuration
spring.datasource.quartz.url=jdbc:postgresql://localhost:5432/quartz
spring.datasource.quartz.username=quartz_user
spring.datasource.quartz.password=quartz_passwordQualifier annotation for specifying a dedicated PlatformTransactionManager for Quartz operations.
/**
* Qualifier annotation for TransactionManager to be injected into Quartz auto-configuration
* Used when you want to use a separate transaction manager for Quartz operations
* Can be applied to secondary transaction manager when another is marked as @Primary
*/
@Target({
ElementType.FIELD,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.TYPE,
ElementType.ANNOTATION_TYPE
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier
public @interface QuartzTransactionManager {
}Usage Examples:
@Configuration
@EnableTransactionManagement
public class TransactionManagerConfiguration {
// Primary transaction manager for application
@Bean
@Primary
public PlatformTransactionManager primaryTransactionManager(
@Primary DataSource primaryDataSource) {
return new DataSourceTransactionManager(primaryDataSource);
}
// Dedicated transaction manager for Quartz
@Bean
@QuartzTransactionManager
public PlatformTransactionManager quartzTransactionManager(
@QuartzDataSource DataSource quartzDataSource) {
DataSourceTransactionManager txManager = new DataSourceTransactionManager(quartzDataSource);
txManager.setNestedTransactionAllowed(true);
return txManager;
}
// JTA transaction manager for distributed transactions
@Bean
@QuartzTransactionManager
public JtaTransactionManager quartzJtaTransactionManager() {
JtaTransactionManager jtaTxManager = new JtaTransactionManager();
jtaTxManager.setAllowCustomIsolationLevels(true);
return jtaTxManager;
}
}
// Using in a custom job with transaction support
@Component
public class TransactionalJob implements Job {
@Autowired
@QuartzTransactionManager
private PlatformTransactionManager quartzTransactionManager;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
TransactionTemplate transactionTemplate =
new TransactionTemplate(quartzTransactionManager);
transactionTemplate.execute(status -> {
// Perform transactional operations
processData();
updateJobStatus();
return null;
});
}
}@Configuration
public class CustomJobFactoryConfiguration {
@Bean
public SchedulerFactoryBeanCustomizer jobFactoryCustomizer(
ApplicationContext applicationContext) {
return schedulerFactoryBean -> {
CustomSpringBeanJobFactory jobFactory = new CustomSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
schedulerFactoryBean.setJobFactory(jobFactory);
};
}
}
public class CustomSpringBeanJobFactory extends SpringBeanJobFactory {
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
// Apply custom initialization logic
if (jobInstance instanceof CustomInitializable) {
((CustomInitializable) jobInstance).initialize();
}
return jobInstance;
}
}@Configuration
public class SchedulerListenerConfiguration {
@Bean
public SchedulerFactoryBeanCustomizer listenerCustomizer() {
return schedulerFactoryBean -> {
schedulerFactoryBean.setSchedulerListeners(
new CustomSchedulerListener()
);
schedulerFactoryBean.setGlobalJobListeners(
new CustomJobListener(),
new AuditJobListener()
);
schedulerFactoryBean.setGlobalTriggerListeners(
new CustomTriggerListener()
);
};
}
}
public class CustomSchedulerListener implements SchedulerListener {
private static final Logger logger = LoggerFactory.getLogger(CustomSchedulerListener.class);
@Override
public void schedulerStarted() {
logger.info("Scheduler started successfully");
// Perform startup tasks
}
@Override
public void schedulerShutdown() {
logger.info("Scheduler shutting down");
// Perform cleanup tasks
}
// Other methods...
}@Configuration
public class QuartzPluginConfiguration {
@Bean
public SchedulerFactoryBeanCustomizer pluginCustomizer() {
return schedulerFactoryBean -> {
Properties properties = new Properties();
// Job initialization plugin
properties.setProperty("org.quartz.plugin.jobInitializer.class",
"org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin");
properties.setProperty("org.quartz.plugin.jobInitializer.fileNames",
"jobs.xml");
properties.setProperty("org.quartz.plugin.jobInitializer.failOnFileNotFound",
"true");
// Logging plugin
properties.setProperty("org.quartz.plugin.triggHistory.class",
"org.quartz.plugins.history.LoggingTriggerHistoryPlugin");
properties.setProperty("org.quartz.plugin.triggHistory.triggerFiredMessage",
"Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}");
schedulerFactoryBean.setQuartzProperties(properties);
};
}
}@Configuration
public class ConditionalQuartzConfiguration {
@Bean
@ConditionalOnProperty(name = "app.quartz.clustering.enabled", havingValue = "true")
public SchedulerFactoryBeanCustomizer clusteringCustomizer() {
return schedulerFactoryBean -> {
Properties clusterProps = new Properties();
clusterProps.setProperty("org.quartz.jobStore.isClustered", "true");
clusterProps.setProperty("org.quartz.jobStore.clusterCheckinInterval", "20000");
clusterProps.setProperty("org.quartz.scheduler.instanceId", "AUTO");
schedulerFactoryBean.setQuartzProperties(clusterProps);
};
}
@Bean
@ConditionalOnMissingBean(name = "quartzDataSource")
public SchedulerFactoryBeanCustomizer memoryJobStoreCustomizer() {
return schedulerFactoryBean -> {
Properties memoryProps = new Properties();
memoryProps.setProperty("org.quartz.jobStore.class",
"org.quartz.simpl.RAMJobStore");
schedulerFactoryBean.setQuartzProperties(memoryProps);
};
}
}@Configuration
public class OrderedCustomizerConfiguration {
@Bean
@Order(1)
public SchedulerFactoryBeanCustomizer dataSourceCustomizer() {
return schedulerFactoryBean -> {
// Configure DataSource first
};
}
@Bean
@Order(2)
public SchedulerFactoryBeanCustomizer propertiesCustomizer() {
return schedulerFactoryBean -> {
// Configure properties after DataSource
};
}
@Bean
@Order(3)
public SchedulerFactoryBeanCustomizer listenersCustomizer() {
return schedulerFactoryBean -> {
// Configure listeners last
};
}
}@Bean
public SchedulerFactoryBeanCustomizer safeCustomizer() {
return schedulerFactoryBean -> {
try {
// Apply customizations
schedulerFactoryBean.setSchedulerName("CustomScheduler");
} catch (Exception e) {
logger.error("Failed to customize scheduler", e);
// Fallback to default configuration
}
};
}Install with Tessl CLI
npx tessl i tessl/maven-org-springframework-boot--spring-boot-starter-quartz