How to conditionally enable or disable scheduled jobs in Spring?

I am defining scheduled jobs with cron style patterns in Spring, using the @Scheduled annotation.

The cron pattern is stored in a config properties file. Actually there are two properties files: one default config, and one profile config that is environment dependent (e.g. dev, test, prod customer 1, prod customer 2 etc.) and overrides some of the default values.

I configured a property placeholder bean in my spring context which allows me to use ${} style placeholders to import values from my properties files.

The job beans looks like this:

@Component
public class ImagesPurgeJob implements Job {

    private Logger logger = Logger.getLogger(this.getClass());

    @Override
    @Transactional(readOnly=true)
    @Scheduled(cron = "${jobs.mediafiles.imagesPurgeJob.schedule}")
    public void execute() {
        //Do something
            //can use DAO or other autowired beans here
    }
}

Relevant parts of my context XML :

<!-- Enable configuration of scheduled tasks via annotations -->
    <task:annotation-driven/>

<!-- Load configuration files and allow '${}' style placeholders -->
    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:config/default-config.properties</value>
                <value>classpath:config/environment-config.properties</value>
            </list>
        </property>
        <property name="ignoreUnresolvablePlaceholders" value="true"/>
        <property name="ignoreResourceNotFound" value="false"/>
    </bean>

I really like this. It's quite simple and clean with minimal XML.

However I have one more requirement: some of these jobs can be totally disabled in some cases.

So, before I used Spring to manage them I created them manually and there is a boolean parameter along with the cron parameter in the config files, to specify if the job has to be enabled or not:

jobs.mediafiles.imagesPurgeJob.enable=true or false
jobs.mediafiles.imagesPurgeJob.schedule=0 0 0/12 * * ?

How can I use this parameter in Spring to conditionally create or just plainly ignore the bean, depending on this config parameter?

One obvious workaround would be to define a cron pattern that would never evaluate, so the job is never executed. But the bean would still be created and the config would be a bit obscure, so I feel there must be a better solution.


Solution 1:

@Component
public class ImagesPurgeJob implements Job {

    private Logger logger = Logger.getLogger(this.getClass());

    @Value("${jobs.mediafiles.imagesPurgeJob.enable}")
    private boolean imagesPurgeJobEnable;

    @Override
    @Transactional(readOnly=true)
    @Scheduled(cron = "${jobs.mediafiles.imagesPurgeJob.schedule}")
    public void execute() {

         //Do something
        //can use DAO or other autowired beans here
        if(imagesPurgeJobEnable){

            Do your conditional job here...

        }
    }
}

Solution 2:

The most efficient way to disable @Scheduled in Spring is to set cron expression to -

@Scheduled(cron = "-")
public void autoEvictAllCache() {
    LOGGER.info("Refresing the Cache Start :: " + new Date());
    activeMQUtility.sendToTopicCacheEviction("ALL");
    LOGGER.info("Refresing the Cache Complete :: " + new Date());
}

From the docs:

CRON_DISABLED

public static final String CRON_DISABLED
A special cron expression value that indicates a disabled trigger: "-". This is primarily meant for use with ${...} placeholders, allowing for external disabling of corresponding scheduled methods.

Since: 5.1 See Also: ScheduledTaskRegistrar.CRON_DISABLED

Solution 3:

You can group schedule methods by conditions into number of services and init them like this:

@Service
@ConditionalOnProperty("yourConditionPropery")
public class SchedulingService {

@Scheduled
public void task1() {...}

@Scheduled
public void task2() {...}

}

Solution 4:

Spring Boot provides @ConditionalOnProperty, which would be perfect if you were using Spring Boot. This annotation is a specialization of @Conditional, introduced with Spring 4.0.0.

Assuming you're just using "regular" spring and not Spring Boot, you could create your own Condition implementation for use with @Conditional that would mimic Spring Boot's @ConditionalOnProperty.