Spring ScheduledTask - start/stop support?
Asked Answered
T

4

7

Is there a way to start or stop a task scheduled using Spring Scheduled Tasks initialized using context file or @Scheduled annotation?

I would like to start the task when required and stop it when the task is no longer needed to be run.

If this is not possible, any alternative to injecting spring variables to a thread?

Tychonn answered 18/8, 2011 at 12:29 Comment(1)
You can find how to stop, start, and list scheduled tasks.Harrus
M
4

Here is an example of starting/stopping a scheduled method in Spring Boot. You can use such APIs:
http:localhost:8080/start - for starting scheduled method with fixed rate 5000 ms
http:localhost:8080/stop - for stopping scheduled method

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.time.Instant;
import java.util.concurrent.ScheduledFuture;

@Configuration
@ComponentScan
@EnableAutoConfiguration    
public class TaskSchedulingApplication {

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

    @Bean
    TaskScheduler threadPoolTaskScheduler() {
        return new ThreadPoolTaskScheduler();
    }
}

@Controller
class ScheduleController {

    public static final long FIXED_RATE = 5000;

    @Autowired
    TaskScheduler taskScheduler;

    ScheduledFuture<?> scheduledFuture;

    @RequestMapping("start")
    ResponseEntity<Void> start() {
        scheduledFuture = taskScheduler.scheduleAtFixedRate(printHour(), FIXED_RATE);

        return new ResponseEntity<Void>(HttpStatus.OK);
    }

    @RequestMapping("stop")
    ResponseEntity<Void> stop() {
        scheduledFuture.cancel(false);
        return new ResponseEntity<Void>(HttpStatus.OK);
    }

    private Runnable printHour() {
        return () -> System.out.println("Hello " + Instant.now().toEpochMilli());
    }

}
Mika answered 27/9, 2017 at 9:4 Comment(0)
J
3

Stopping registered @Scheduled beans is not standard feature since the access to them is private in the org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.

If you need to manage the time they run you need to register them programmatically (TriggerTask): see the documentation to org.springframework.scheduling.annotation.SchedulingConfigurer. In the type org.springframework.scheduling.config.TriggerTask there is the method which returns type of org.springframework.scheduling.Trigger. There you can manage next execution time.

TriggerTasks could be beans in the case of programmatical registration.

Jacklynjackman answered 29/9, 2017 at 9:40 Comment(0)
B
1

Have the @Scheduled method look for a variable held in Application state or ServletContext, or from a value stored in the DB. If the value is TRUE, proceed with the task; if FALSE, don't start. This setup will control the scheduled run.

If you want to also be able to fire the task at will, reference the task's method from a Controller; that way you can fire it at will. Additionally, if its a longer running task, create a second method annotated @Async and call that method from your Controller so that it runs in its own thread.

Bergeron answered 18/8, 2011 at 13:14 Comment(4)
When you say "don't start", this cannot be done, Spring will run the function every X seconds. There is no "start" mechanism.Tychonn
I meant exit the method, or do something else so that the method's business function is not executed.Bergeron
Well, I think the method will keep execute the function even though it exits. I hope there is a better solution.Tychonn
I am also looking for getting a hand on something akin to TimerTask#cancel()Sunburn
A
0

There is a pretty simple way of doing it (Spring Boot 2.3.12.RELEASE). I have a rule that all my job classes must implement Runnable and run method has @Scheduled annotation. Method proceed returns a new ScheduledTask object that can be suspended. Please note that postProcessBeforeDestruction removes "suspended" scheduled tasks from internal map hence we need to create a new one by calling postProcessAfterInitialization

    @RequiredArgsConstructor
    public class CustomScheduledTaskRegistrar extends ScheduledTaskRegistrar {

        private final JobTrackingManager jobTrackingManager; // Custom class to keep records on ScheduledTask objects

        @Override
        public void afterPropertiesSet() {
            super.afterPropertiesSet();
            jobTrackingManager.registerScheduledTasks();
        }

    } 


    public class CustomScheduledAnnotationBeanPostProcessor extends ScheduledAnnotationBeanPostProcessor {

        public CustomScheduledAnnotationBeanPostProcessor(CustomScheduledTaskRegistrar customScheduledTaskRegistrar) {
            super(customScheduledTaskRegistrar);
        }

        public void suspend(ScheduledTask scheduledTask) {
            Object bean = ((ScheduledMethodRunnable) scheduledTask.getTask().getRunnable()).getTarget();
            postProcessBeforeDestruction(bean, "Ignored");
        }

        @SneakyThrows
        public ScheduledTask proceed(ScheduledTask scheduledTask) {
            Object bean = ((ScheduledMethodRunnable) scheduledTask.getTask().getRunnable()).getTarget();
            if (!requiresDestruction(bean)) {
                String beanName = AopProxyUtils.ultimateTargetClass(bean).getSimpleName();
                postProcessAfterInitialization(bean, beanName);
            }
            return getScheduledTasks().stream()
            .filter(st -> ((ScheduledMethodRunnable) st.getTask().getRunnable()).getTarget().equals(bean))
            .findFirst()
            .orElseThrow(() -> new IllegalStateException("Unable to find the scheduled task"));
        }

    }
Abruzzi answered 1/10, 2021 at 9:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.