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

Viewed 673

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
2 Answers

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)

No. But I'd suggest you to define all job beans as conditional like following:

@ConditionalOnProperty(value="jobs.enabled", havingValue = "true", matchIfMissing = true)
@Component
@Log4j2
public class GreetJob {
    ...
}

In such case all job beans will be enabled by default, even if property jobs.enabled is not defined. Do disable all jobs at once you can define property in your application properties file

jobs.enabled=false

Or define this property in the command line:

-Djobs.enabled=false
Related