Search code examples
javaspringspring-bootcronspring-scheduled

Is there a global flag in Spring Boot to disable all the scheduled jobs?


I have two jobs, the first one runs every 2 seconds and the second one runs every 10 seconds. I have their cron expressions set in application.properties and aware of the fact that each job can be disabled by modifying it's cron expression to "-" in application.properties file.

But would really like to know if there is a global flag available in Spring Boot that would disable all jobs in single shot?

Tried following and got error at server startup.

pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.6.RELEASE</version>
    <relativePath />
</parent>

application.properties

#global flag for all jobs
spring.enable.scheduling=false
jobs.greet.cron=0/2 * * * * ? 
jobs.email.cron=0/10 * * * * ?

DemoApplication.java

@SpringBootApplication
@EnableScheduling
@ConditionalOnProperty(name = "spring.enable.scheduling", matchIfMissing = true, havingValue = "true")
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

GreetJob.java

@Component
@Log4j2
public class GreetJob {

    @Scheduled(cron = "${jobs.greet.cron}")
    public void greet() {
        log.info("Starting greet now...");
    }

}

EmailJob.java

@Component
@Log4j2
public class EmailJob {

    @Scheduled(cron = "${jobs.email.cron}")
    public void sendEmail() {
        log.info("Sending email now...");
    }

}

Exception:

2020-05-23 15:51:04,536 ERROR org.springframework.boot.SpringApplication [restartedMain] Application run failed
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:156)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:544)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
    at com.example.DemoApplication.main(DemoApplication.java:27)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getWebServerFactory(ServletWebServerApplicationContext.java:203)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:179)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:153)
    ... 13 common frames omitted

Solution

  • By default, there is no such an option, but you can provide the facility with the "global" flag if you put an @EnableScheduling on an artificially created configuration with @Conditional on it. This way you won't need to change already defined Scheduled Jobs by putting @Conditional on them (GreetJob and EmailJob in your case) and will provide a single management point for this facility:

    So to create that "artificial" configuration, create the following:

    @Configuration
    @EnableScheduling
    @ConditionalOnProperty(name="jobs.enabled", havingValue = "true")
    public class ConditionalEnableScheduling {
    }
    

    This way if you start the application with --jobs.enabled the conditional will "trigger" and the configuration will load so that the "scheduling infra" will be initialized

    General notes (just for the sake of completeness of the answer):

    • Remove @EnableScheduling from the main class
    • Make sure this configuration is scanned (place it next to the main class for example)