I have been investigating how to change the frequency of a job on runtime with Java 8 and spring. This question was very useful but it did not totally solve my issue.
I can now configure the date when to job should be executed next. But If set the delay to 1 year, then I need to wait 1 year before the new configuration in taken into account.
My idea would be to stop the scheduled task if the configuration value is changed (so from another class). Then recalculate the next time the task should be executed. Perhaps there is an easier way of doing this.
Here is the code I have so far.
@Configuration
@EnableScheduling
public class RequestSchedulerConfig implements SchedulingConfigurer {
@Autowired
SchedulerConfigService schedulerConfigService;
@Bean
public RequestScheduler myBean() {
return new RequestScheduler();
}
@Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(100);
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
taskRegistrar.addTriggerTask(
new Runnable() {
@Override public void run() {
myBean().startReplenishmentComputation();
}
},
new Trigger() {
@Override public Date nextExecutionTime(TriggerContext triggerContext) {
Duration d = schedulerConfigService.getIntervalFromDB();
return DateTime.now().plus(d).toDate();
}
}
);
}
}
This would be what I would like to do.
@RestController
@RequestMapping("/api/config/scheduler")
public class RequestSchedulerController {
@Autowired
ApplicationConfigWrapper applicationConfigWrapper;
@RequestMapping("/set/")
@ResponseBody
public String setRequestSchedulerConfig(@RequestParam(value = "frequency", defaultValue = "") final String frequencyInSeconds){
changeValueInDb(frequencyInSeconds);
myJob.restart();
return "Yeah";
}
}
One simple approach is to only ever add new tasks, not to try and cancel or restart the scheduler.
Each time the configuration changes, just add a new task with its new configuration.
Then, whenever a task runs, it must first check some state (by querying database, or lookup up in a concurrent map, or whatever) to decide if it is the latest version. If it is, then it should proceed. Otherwise, it should end immediately.
The only downside is that if you are changing job configuration frequently compared to how often they run, then of course the list of scheduled tasks will keep growing in memory.
What about using
Set<ScheduledTask> ScheduledTaskRegistrar.getScheduledTasks()
to get all schedules tasks and callingScheduledTask::cancel()
? or maybe executingThreadPoolTaskScheduler::shutdown()
and recreating ThreadPoolTaskScheduler and setting it again in ScheduledTaskRegistrar ?TaskScheduler
. This will hold as state variables allScheduledFuture
s, likeprivate ScheduledFuture job1;
job1
.Future
(e.gjob1
) and then start it again with the new scheduling data.The key idea here is to get control on the
Future
s as they are created, so to save them in some state variables, so that when something in scheduling data changes, you can cancel them.Here is the working code:
applicationContext.xml
The singleton bean, that holds the
Future
s