Tomer Amir
Tomer Amir

Reputation: 1585

Spring Boot - Alternative to Time.schedule?

I want to do something like javascript's setInterval(function, interval)/setTimeout(function, timeout) in Spring Boot.

I found the @Scheduled annotation that has the fixedRate argument, but as an annotation I cannot change the rate dynamically (Or can I?)

For now I am using java.util.Timer, but I would rather use Spring. Is there a way?

Can I get a Scheduler instance and work with it dynamically?

thanks!

Upvotes: 3

Views: 4764

Answers (3)

alexbt
alexbt

Reputation: 17045

You may use a Trigger which lets you dynamically control the next execution. You need to implement SchedulingConfigurer, another answer covers exactly this:

Scheduling a job with Spring programmatically (with fixedRate set dynamically)

EDIT to answer comments:

nextExecutionTime is called on and on and on... The next time the task (and nextExecutionTime) is called is defined by this:

nextExecutionTime.setTime(lastActualExecutionTime != null ? lastActualExecutionTime : new Date());
nextExecutionTime.add(Calendar.MILLISECOND, numberOfMillisecondsBeforeCallingTheTask);

All you need to do is have this numberOfMillisecondsBeforeCallingTheTask value changed.

Example:

@RestController
public class MyController {

    public static int triggerDelay = 1000;

    @RequestMapping("/changetrigger/{val}")
    public void test(@PathVariable int val){
        this.triggerDelay = val;
    }
}
@SpringBootApplication
@EnableScheduling
public class Launcher implements SchedulingConfigurer{

    public static void main(String[] args){
        new SpringApplicationBuilder() //
        .sources(Launcher.class)//
        .run(args);
    }

    @Bean(destroyMethod = "shutdown")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(100);
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
         ;
        taskRegistrar.addTriggerTask(new TriggerTask(new Runnable() {
            @Override
            public void run() {
                System.out.println("blah");
                System.out.println(System.currentTimeMillis());
            }
        }, new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                Calendar nextExecutionTime = new GregorianCalendar();
                nextExecutionTime.setTime(new Date());
                nextExecutionTime.add(Calendar.MILLISECOND, MyController.triggerDelay);
                System.out.println(System.currentTimeMillis());
                return nextExecutionTime.getTime();
            }}));
    }
}

Notice how the dynamic value MyController.triggerDelay is used for the next execution. So if you change the number, the next execution time will be changed. You'll see if you put a breakpoint inside nextExecutionTime.

Upvotes: 3

Tomer Amir
Tomer Amir

Reputation: 1585

Found a solution that works for my case.

In Main.java:

@SpringBootApplication
@ConfigurationProperties
@EnableScheduling
public class Main {
    @Bean
    ThreadPoolTaskScheduler taskScheduler() {
        return new ThreadPoolTaskScheduler();
    }

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

In Service.java (Called from a rest controller):

@Service
public class Service {
    private static final Logger log = LoggerFactory.getLogger(Service.class);
    private final TaskScheduler scheduler;

    @Autowired
    public Service(TaskScheduler scheduler) {
        this.scheduler = scheduler;
    }

    public void startTask(int inteval) {
        scheduler.schedule(() -> log.info("Doing work"), triggerContext -> {
            if (some_condition) {
                ZonedDateTime now = ZonedDateTime.now();

                return Date.from(now.plusSeconds(interval).toInstant());
            } else {
                // Stop the execution
                return null;
            }
        });
    }
}

This solution works, but I'm not sure it is the correct way.

You are welcome to comment below, and I might change the solution if I get a suggestion I find helpful.

Upvotes: 0

Liping Huang
Liping Huang

Reputation: 4476

You can use @Scheduled(fixedRateString = "${spring.boot.schedule.rate}") for your case, where the spring.boot.schedule.rate is the external properties in application.properties

spring.boot.schedule.rate=5000

Misunderstand the question, above is just the externalize the properties.

For the dynamic solution, maybe this should be work, using the spEL in the annonation:

@Service
public class ScheduledService {
    @Autowired
    private FixRateProperty fixRateProperty;

    @Scheduled(fixedRateString = "#{fixRateProperty.fixRate}")
    private void reportCurrentTime() {
        System.out.println(new Date());;
    }
}

This is the FixRateProperty

@Component
public class FixRateProperty {
    private Long fixRate = 500L;

    public Long getFixRate() {
        return fixRate;
    }

    public void setFixRate(Long fixRate) {
        this.fixRate = fixRate;
    }
}

so you can externalize the rate in the properties or set the fixRate somewhere.

Upvotes: 0

Related Questions