Reputation: 1585
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
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
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
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