I'll leave another option here for completeness sake, but OP's solution looks the cleanest for his reactive case. Spring's Trigger is used to calculate the next execution time. In this case it has to know if the task was successful or not. I did not find any way to notify the trigger through the API, my best idea was to use instance variable.
public class DynamicTrigger implements Trigger {
private boolean isTaskSuccessFul = true;
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
Date last = triggerContext.lastCompletionTime();
Instant lastCompleted = last != null ? last.toInstant() : Instant.now();
int seconds = this.isTaskSuccessFul ? 10 : 3;
return Date.from(lastCompleted.plusSeconds(seconds));
}
public void setTaskSuccessFul(boolean taskSuccessFul) {
this.isTaskSuccessFul = taskSuccessFul;
}
}
The task, with extremely simple simulation of every second task failing:
public class ThrowingRunnable implements Runnable {
private int count = 1;
@Override
public void run() {
int current = this.count;
if (this.count++ % 2 == 0) {
System.out.println("Task failed - " + LocalDateTime.now() + " - " + current);
throw new RuntimeException();
}
System.out.println("Task success - " + LocalDateTime.now() + " - " + current);
}
}
Another runnable, to wrap the task itself, handle the exception and notify trigger about success or failure:
public class WrappingRunnable implements Runnable {
private final Runnable wrappedRunnable;
private final DynamicTrigger trigger;
public WrappingRunnable(Runnable wrappedRunnable, DynamicTrigger trigger) {
this.wrappedRunnable = wrappedRunnable;
this.trigger = trigger;
}
@Override
public void run() {
try {
this.wrappedRunnable.run();
this.trigger.setTaskSuccessFul(true);
} catch (RuntimeException exc) {
this.trigger.setTaskSuccessFul(false);
}
}
}
Another possibility is to notify the trigger for failure through the error handler, but that would require transferring it with a custom exception and casting, i don't think it's worth it.
And scheduling the trigger manually:
@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ConcurrentTaskScheduler taskScheduler = new ConcurrentTaskScheduler(Executors.newSingleThreadScheduledExecutor());
taskScheduler.setErrorHandler(Throwable::printStackTrace);
taskRegistrar.setTaskScheduler(taskScheduler);
DynamicTrigger trigger = new DynamicTrigger();
WrappingRunnable runnable = new WrappingRunnable(new ThrowingRunnable(), trigger);
taskRegistrar.addTriggerTask(runnable, trigger);
}
}
Edit: Wrapping reactive code into Runnable can be like this:
public class MonoRunnable implements Runnable {
private final MyService myService;
private final DynamicTrigger trigger;
public MonoRunnable(MyService myService, DynamicTrigger trigger) {
this.myService = myService;
this.trigger = trigger;
}
@Override
public void run() {
this.myService.fetchData().doOnSuccess(data -> {
//do stuff with data
this.trigger.setTaskSuccessFul(true);
}).doOnError(throwable -> {
//do stuff with error
this.trigger.setTaskSuccessFul(false);
}).subscribe();
}
}
I skipped subscribeOn(), cause i am not sure of the effects the Worker will have, considering the task will be scheduled with spring scheduler. On success and on error functions will take care of notifying the Trigger, and WrappingRunnable won't be needed, and we can directly register the mono task:
MonoRunnable runnable = new MonoRunnable(this.myService, trigger);
taskRegistrar.addTriggerTask(runnable, trigger);