Add execution metadata to scheduled tasks actuator endpoint
As of spring-projects/spring-framework#24560, Spring provides additional metadata for scheduled tasks: * next execution time * last execution outcome (including status, time and raised exception) This commit leverages this information to enhance the existing `scheduledtasks` Actuator endpoint. Closes gh-17585
This commit is contained in:
@@ -27,11 +27,13 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.restdocs.payload.FieldDescriptor;
|
||||
import org.springframework.restdocs.payload.JsonFieldType;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.TriggerContext;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.concurrent.SimpleAsyncTaskScheduler;
|
||||
import org.springframework.scheduling.config.ScheduledTaskHolder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -57,9 +59,12 @@ class ScheduledTasksEndpointDocumentationTests extends MockMvcEndpointDocumentat
|
||||
"com.example.Processor")),
|
||||
responseFields(fieldWithPath("cron").description("Cron tasks, if any."),
|
||||
targetFieldWithPrefix("cron.[]."),
|
||||
nextExecutionWithPrefix("cron.[].").description("Time of the next scheduled execution."),
|
||||
fieldWithPath("cron.[].expression").description("Cron expression."),
|
||||
fieldWithPath("fixedDelay").description("Fixed delay tasks, if any."),
|
||||
targetFieldWithPrefix("fixedDelay.[]."), initialDelayWithPrefix("fixedDelay.[]."),
|
||||
nextExecutionWithPrefix("fixedDelay.[].")
|
||||
.description("Time of the next scheduled execution."),
|
||||
fieldWithPath("fixedDelay.[].interval")
|
||||
.description("Interval, in milliseconds, between the end of the last"
|
||||
+ " execution and the start of the next."),
|
||||
@@ -68,9 +73,15 @@ class ScheduledTasksEndpointDocumentationTests extends MockMvcEndpointDocumentat
|
||||
fieldWithPath("fixedRate.[].interval")
|
||||
.description("Interval, in milliseconds, between the start of each execution."),
|
||||
initialDelayWithPrefix("fixedRate.[]."),
|
||||
nextExecutionWithPrefix("fixedRate.[].")
|
||||
.description("Time of the next scheduled execution."),
|
||||
fieldWithPath("custom").description("Tasks with custom triggers, if any."),
|
||||
targetFieldWithPrefix("custom.[]."),
|
||||
fieldWithPath("custom.[].trigger").description("Trigger for the task."))));
|
||||
fieldWithPath("custom.[].trigger").description("Trigger for the task."))
|
||||
.andWithPrefix("*.[].",
|
||||
fieldWithPath("lastExecution").description("Last execution of this task, if any.")
|
||||
.optional())
|
||||
.andWithPrefix("*.[].lastExecution.", lastExecution())));
|
||||
}
|
||||
|
||||
private FieldDescriptor targetFieldWithPrefix(String prefix) {
|
||||
@@ -81,6 +92,22 @@ class ScheduledTasksEndpointDocumentationTests extends MockMvcEndpointDocumentat
|
||||
return fieldWithPath(prefix + "initialDelay").description("Delay, in milliseconds, before first execution.");
|
||||
}
|
||||
|
||||
private FieldDescriptor nextExecutionWithPrefix(String prefix) {
|
||||
return fieldWithPath(prefix + "nextExecution.time").description("Time of the next scheduled execution.");
|
||||
}
|
||||
|
||||
private FieldDescriptor[] lastExecution() {
|
||||
return new FieldDescriptor[] {
|
||||
fieldWithPath("status").description("Status of the last execution (STARTED, SUCCESS, ERROR)."),
|
||||
fieldWithPath("time").description("Time of the last execution.").type(JsonFieldType.STRING),
|
||||
fieldWithPath("exception.type").description("Exception type thrown by the task, if any.")
|
||||
.type(JsonFieldType.STRING)
|
||||
.optional(),
|
||||
fieldWithPath("exception.message").description("Message of the exception thrown by the task, if any.")
|
||||
.type(JsonFieldType.STRING)
|
||||
.optional() };
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableScheduling
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
@@ -96,7 +123,7 @@ class ScheduledTasksEndpointDocumentationTests extends MockMvcEndpointDocumentat
|
||||
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 5000, initialDelay = 5000)
|
||||
@Scheduled(fixedDelay = 5000, initialDelay = 0)
|
||||
void purge() {
|
||||
|
||||
}
|
||||
@@ -108,7 +135,10 @@ class ScheduledTasksEndpointDocumentationTests extends MockMvcEndpointDocumentat
|
||||
|
||||
@Bean
|
||||
SchedulingConfigurer schedulingConfigurer() {
|
||||
return (registrar) -> registrar.addTriggerTask(new CustomTriggeredRunnable(), new CustomTrigger());
|
||||
return (registrar) -> {
|
||||
registrar.setTaskScheduler(new TestTaskScheduler());
|
||||
registrar.addTriggerTask(new CustomTriggeredRunnable(), new CustomTrigger());
|
||||
};
|
||||
}
|
||||
|
||||
static class CustomTrigger implements Trigger {
|
||||
@@ -124,7 +154,18 @@ class ScheduledTasksEndpointDocumentationTests extends MockMvcEndpointDocumentat
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
throw new IllegalStateException("Failed while running custom task");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestTaskScheduler extends SimpleAsyncTaskScheduler {
|
||||
|
||||
TestTaskScheduler() {
|
||||
setThreadNamePrefix("test-");
|
||||
// do not log task errors
|
||||
setErrorHandler((throwable) -> {
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,14 +17,12 @@
|
||||
package org.springframework.boot.actuate.scheduling;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
@@ -42,15 +40,20 @@ import org.springframework.scheduling.config.IntervalTask;
|
||||
import org.springframework.scheduling.config.ScheduledTask;
|
||||
import org.springframework.scheduling.config.ScheduledTaskHolder;
|
||||
import org.springframework.scheduling.config.Task;
|
||||
import org.springframework.scheduling.config.TaskExecutionOutcome;
|
||||
import org.springframework.scheduling.config.TaskExecutionOutcome.Status;
|
||||
import org.springframework.scheduling.config.TriggerTask;
|
||||
import org.springframework.scheduling.support.CronTrigger;
|
||||
import org.springframework.scheduling.support.PeriodicTrigger;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link Endpoint @Endpoint} to expose information about an application's scheduled
|
||||
* tasks.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Brian Clozel
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Endpoint(id = "scheduledtasks")
|
||||
@@ -65,12 +68,16 @@ public class ScheduledTasksEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public ScheduledTasksDescriptor scheduledTasks() {
|
||||
Map<TaskType, List<TaskDescriptor>> descriptionsByType = this.scheduledTaskHolders.stream()
|
||||
.flatMap((holder) -> holder.getScheduledTasks().stream())
|
||||
.map(ScheduledTask::getTask)
|
||||
.map(TaskDescriptor::of)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.groupingBy(TaskDescriptor::getType));
|
||||
MultiValueMap<TaskType, TaskDescriptor> descriptionsByType = new LinkedMultiValueMap<>();
|
||||
for (ScheduledTaskHolder holder : this.scheduledTaskHolders) {
|
||||
for (ScheduledTask scheduledTask : holder.getScheduledTasks()) {
|
||||
TaskType taskType = TaskType.forTask(scheduledTask);
|
||||
if (taskType != null) {
|
||||
TaskDescriptor descriptor = taskType.createDescriptor(scheduledTask);
|
||||
descriptionsByType.add(descriptor.getType(), descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ScheduledTasksDescriptor(descriptionsByType);
|
||||
}
|
||||
|
||||
@@ -117,45 +124,16 @@ public class ScheduledTasksEndpoint {
|
||||
*/
|
||||
public abstract static class TaskDescriptor {
|
||||
|
||||
private static final Map<Class<? extends Task>, Function<Task, TaskDescriptor>> DESCRIBERS = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
DESCRIBERS.put(FixedRateTask.class, (task) -> new FixedRateTaskDescriptor((FixedRateTask) task));
|
||||
DESCRIBERS.put(FixedDelayTask.class, (task) -> new FixedDelayTaskDescriptor((FixedDelayTask) task));
|
||||
DESCRIBERS.put(CronTask.class, (task) -> new CronTaskDescriptor((CronTask) task));
|
||||
DESCRIBERS.put(TriggerTask.class, (task) -> describeTriggerTask((TriggerTask) task));
|
||||
}
|
||||
|
||||
private final TaskType type;
|
||||
|
||||
private final ScheduledTask scheduledTask;
|
||||
|
||||
private final RunnableDescriptor runnable;
|
||||
|
||||
private static TaskDescriptor of(Task task) {
|
||||
return DESCRIBERS.entrySet()
|
||||
.stream()
|
||||
.filter((entry) -> entry.getKey().isInstance(task))
|
||||
.map((entry) -> entry.getValue().apply(task))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private static TaskDescriptor describeTriggerTask(TriggerTask triggerTask) {
|
||||
Trigger trigger = triggerTask.getTrigger();
|
||||
if (trigger instanceof CronTrigger cronTrigger) {
|
||||
return new CronTaskDescriptor(triggerTask, cronTrigger);
|
||||
}
|
||||
if (trigger instanceof PeriodicTrigger periodicTrigger) {
|
||||
if (periodicTrigger.isFixedRate()) {
|
||||
return new FixedRateTaskDescriptor(triggerTask, periodicTrigger);
|
||||
}
|
||||
return new FixedDelayTaskDescriptor(triggerTask, periodicTrigger);
|
||||
}
|
||||
return new CustomTriggerTaskDescriptor(triggerTask);
|
||||
}
|
||||
|
||||
protected TaskDescriptor(TaskType type, Runnable runnable) {
|
||||
protected TaskDescriptor(ScheduledTask scheduledTask, TaskType type) {
|
||||
this.scheduledTask = scheduledTask;
|
||||
this.type = type;
|
||||
this.runnable = new RunnableDescriptor(runnable);
|
||||
this.runnable = new RunnableDescriptor(scheduledTask.getTask().getRunnable());
|
||||
}
|
||||
|
||||
private TaskType getType() {
|
||||
@@ -166,6 +144,130 @@ public class ScheduledTasksEndpoint {
|
||||
return this.runnable;
|
||||
}
|
||||
|
||||
public final NextExecution getNextExecution() {
|
||||
Instant nextExecution = this.scheduledTask.nextExecution();
|
||||
if (nextExecution != null) {
|
||||
return new NextExecution(nextExecution);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public final LastExecution getLastExecution() {
|
||||
TaskExecutionOutcome lastExecutionOutcome = this.scheduledTask.getTask().getLastExecutionOutcome();
|
||||
if (lastExecutionOutcome.status() != Status.NONE) {
|
||||
return new LastExecution(lastExecutionOutcome);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final class NextExecution {
|
||||
|
||||
private final Instant time;
|
||||
|
||||
public NextExecution(Instant time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public Instant getTime() {
|
||||
return this.time;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final class LastExecution {
|
||||
|
||||
private final TaskExecutionOutcome lastExecutionOutcome;
|
||||
|
||||
private LastExecution(TaskExecutionOutcome lastExecutionOutcome) {
|
||||
this.lastExecutionOutcome = lastExecutionOutcome;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return this.lastExecutionOutcome.status();
|
||||
}
|
||||
|
||||
public Instant getTime() {
|
||||
return this.lastExecutionOutcome.executionTime();
|
||||
}
|
||||
|
||||
public ExceptionInfo getException() {
|
||||
Throwable throwable = this.lastExecutionOutcome.throwable();
|
||||
if (throwable != null) {
|
||||
return new ExceptionInfo(throwable);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final class ExceptionInfo {
|
||||
|
||||
private final Throwable throwable;
|
||||
|
||||
private ExceptionInfo(Throwable throwable) {
|
||||
this.throwable = throwable;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return this.throwable.getClass().getName();
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.throwable.getMessage();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private enum TaskType {
|
||||
|
||||
CRON(CronTask.class,
|
||||
(scheduledTask) -> new CronTaskDescriptor(scheduledTask, (CronTask) scheduledTask.getTask())),
|
||||
FIXED_DELAY(FixedDelayTask.class,
|
||||
(scheduledTask) -> new FixedDelayTaskDescriptor(scheduledTask,
|
||||
(FixedDelayTask) scheduledTask.getTask())),
|
||||
FIXED_RATE(FixedRateTask.class,
|
||||
(scheduledTask) -> new FixedRateTaskDescriptor(scheduledTask, (FixedRateTask) scheduledTask.getTask())),
|
||||
CUSTOM_TRIGGER(TriggerTask.class, TaskType::describeTriggerTask);
|
||||
|
||||
final Class<?> taskClass;
|
||||
|
||||
final Function<ScheduledTask, TaskDescriptor> describer;
|
||||
|
||||
TaskType(Class<?> taskClass, Function<ScheduledTask, TaskDescriptor> describer) {
|
||||
this.taskClass = taskClass;
|
||||
this.describer = describer;
|
||||
}
|
||||
|
||||
static TaskType forTask(ScheduledTask scheduledTask) {
|
||||
for (TaskType taskType : TaskType.values()) {
|
||||
if (taskType.taskClass.isInstance(scheduledTask.getTask())) {
|
||||
return taskType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
TaskDescriptor createDescriptor(ScheduledTask scheduledTask) {
|
||||
return this.describer.apply(scheduledTask);
|
||||
}
|
||||
|
||||
private static TaskDescriptor describeTriggerTask(ScheduledTask scheduledTask) {
|
||||
TriggerTask triggerTask = (TriggerTask) scheduledTask.getTask();
|
||||
Trigger trigger = triggerTask.getTrigger();
|
||||
if (trigger instanceof CronTrigger cronTrigger) {
|
||||
return new CronTaskDescriptor(scheduledTask, triggerTask, cronTrigger);
|
||||
}
|
||||
if (trigger instanceof PeriodicTrigger periodicTrigger) {
|
||||
if (periodicTrigger.isFixedRate()) {
|
||||
return new FixedRateTaskDescriptor(scheduledTask, triggerTask, periodicTrigger);
|
||||
}
|
||||
return new FixedDelayTaskDescriptor(scheduledTask, triggerTask, periodicTrigger);
|
||||
}
|
||||
return new CustomTriggerTaskDescriptor(scheduledTask);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,14 +279,15 @@ public class ScheduledTasksEndpoint {
|
||||
|
||||
private final long interval;
|
||||
|
||||
protected IntervalTaskDescriptor(TaskType type, IntervalTask task) {
|
||||
super(type, task.getRunnable());
|
||||
this.initialDelay = task.getInitialDelayDuration().toMillis();
|
||||
this.interval = task.getIntervalDuration().toMillis();
|
||||
protected IntervalTaskDescriptor(ScheduledTask scheduledTask, TaskType type, IntervalTask intervalTask) {
|
||||
super(scheduledTask, type);
|
||||
this.initialDelay = intervalTask.getInitialDelayDuration().toMillis();
|
||||
this.interval = intervalTask.getIntervalDuration().toMillis();
|
||||
}
|
||||
|
||||
protected IntervalTaskDescriptor(TaskType type, TriggerTask task, PeriodicTrigger trigger) {
|
||||
super(type, task.getRunnable());
|
||||
protected IntervalTaskDescriptor(ScheduledTask scheduledTask, TaskType type, TriggerTask task,
|
||||
PeriodicTrigger trigger) {
|
||||
super(scheduledTask, type);
|
||||
Duration initialDelayDuration = trigger.getInitialDelayDuration();
|
||||
this.initialDelay = (initialDelayDuration != null) ? initialDelayDuration.toMillis() : 0;
|
||||
this.interval = trigger.getPeriodDuration().toMillis();
|
||||
@@ -206,12 +309,12 @@ public class ScheduledTasksEndpoint {
|
||||
*/
|
||||
public static final class FixedDelayTaskDescriptor extends IntervalTaskDescriptor {
|
||||
|
||||
private FixedDelayTaskDescriptor(FixedDelayTask task) {
|
||||
super(TaskType.FIXED_DELAY, task);
|
||||
private FixedDelayTaskDescriptor(ScheduledTask scheduledTask, FixedDelayTask task) {
|
||||
super(scheduledTask, TaskType.FIXED_DELAY, task);
|
||||
}
|
||||
|
||||
private FixedDelayTaskDescriptor(TriggerTask task, PeriodicTrigger trigger) {
|
||||
super(TaskType.FIXED_DELAY, task, trigger);
|
||||
private FixedDelayTaskDescriptor(ScheduledTask scheduledTask, TriggerTask task, PeriodicTrigger trigger) {
|
||||
super(scheduledTask, TaskType.FIXED_DELAY, task, trigger);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -222,12 +325,12 @@ public class ScheduledTasksEndpoint {
|
||||
*/
|
||||
public static final class FixedRateTaskDescriptor extends IntervalTaskDescriptor {
|
||||
|
||||
private FixedRateTaskDescriptor(FixedRateTask task) {
|
||||
super(TaskType.FIXED_RATE, task);
|
||||
private FixedRateTaskDescriptor(ScheduledTask scheduledTask, FixedRateTask task) {
|
||||
super(scheduledTask, TaskType.FIXED_RATE, task);
|
||||
}
|
||||
|
||||
private FixedRateTaskDescriptor(TriggerTask task, PeriodicTrigger trigger) {
|
||||
super(TaskType.FIXED_RATE, task, trigger);
|
||||
private FixedRateTaskDescriptor(ScheduledTask scheduledTask, TriggerTask task, PeriodicTrigger trigger) {
|
||||
super(scheduledTask, TaskType.FIXED_RATE, task, trigger);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -240,13 +343,13 @@ public class ScheduledTasksEndpoint {
|
||||
|
||||
private final String expression;
|
||||
|
||||
private CronTaskDescriptor(CronTask task) {
|
||||
super(TaskType.CRON, task.getRunnable());
|
||||
this.expression = task.getExpression();
|
||||
private CronTaskDescriptor(ScheduledTask scheduledTask, CronTask cronTask) {
|
||||
super(scheduledTask, TaskType.CRON);
|
||||
this.expression = cronTask.getExpression();
|
||||
}
|
||||
|
||||
private CronTaskDescriptor(TriggerTask task, CronTrigger trigger) {
|
||||
super(TaskType.CRON, task.getRunnable());
|
||||
private CronTaskDescriptor(ScheduledTask scheduledTask, TriggerTask triggerTask, CronTrigger trigger) {
|
||||
super(scheduledTask, TaskType.CRON);
|
||||
this.expression = trigger.getExpression();
|
||||
}
|
||||
|
||||
@@ -263,9 +366,10 @@ public class ScheduledTasksEndpoint {
|
||||
|
||||
private final String trigger;
|
||||
|
||||
private CustomTriggerTaskDescriptor(TriggerTask task) {
|
||||
super(TaskType.CUSTOM_TRIGGER, task.getRunnable());
|
||||
this.trigger = task.getTrigger().toString();
|
||||
private CustomTriggerTaskDescriptor(ScheduledTask scheduledTask) {
|
||||
super(scheduledTask, TaskType.CUSTOM_TRIGGER);
|
||||
TriggerTask triggerTask = (TriggerTask) scheduledTask.getTask();
|
||||
this.trigger = triggerTask.getTrigger().toString();
|
||||
}
|
||||
|
||||
public String getTrigger() {
|
||||
@@ -291,12 +395,6 @@ public class ScheduledTasksEndpoint {
|
||||
|
||||
}
|
||||
|
||||
private enum TaskType {
|
||||
|
||||
CRON, CUSTOM_TRIGGER, FIXED_DELAY, FIXED_RATE
|
||||
|
||||
}
|
||||
|
||||
static class ScheduledTasksEndpointRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();
|
||||
|
||||
@@ -31,8 +31,10 @@ import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.CronTa
|
||||
import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.CustomTriggerTaskDescriptor;
|
||||
import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.FixedDelayTaskDescriptor;
|
||||
import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.FixedRateTaskDescriptor;
|
||||
import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.LastExecution;
|
||||
import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.ScheduledTasksDescriptor;
|
||||
import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.ScheduledTasksEndpointRuntimeHints;
|
||||
import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.TaskDescriptor;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -42,6 +44,7 @@ import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.config.ScheduledTaskHolder;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
import org.springframework.scheduling.config.TaskExecutionOutcome.Status;
|
||||
import org.springframework.scheduling.support.CronTrigger;
|
||||
import org.springframework.scheduling.support.PeriodicTrigger;
|
||||
|
||||
@@ -52,6 +55,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Moritz Halbritter
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class ScheduledTasksEndpointTests {
|
||||
|
||||
@@ -68,6 +72,8 @@ class ScheduledTasksEndpointTests {
|
||||
CronTaskDescriptor description = (CronTaskDescriptor) tasks.getCron().get(0);
|
||||
assertThat(description.getExpression()).isEqualTo("0 0 0/3 1/1 * ?");
|
||||
assertThat(description.getRunnable().getTarget()).isEqualTo(CronScheduledMethod.class.getName() + ".cron");
|
||||
assertThat(description.getNextExecution().getTime()).isInTheFuture();
|
||||
assertThat(description.getLastExecution()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -81,6 +87,7 @@ class ScheduledTasksEndpointTests {
|
||||
CronTaskDescriptor description = (CronTaskDescriptor) tasks.getCron().get(0);
|
||||
assertThat(description.getExpression()).isEqualTo("0 0 0/6 1/1 * ?");
|
||||
assertThat(description.getRunnable().getTarget()).contains(CronTriggerRunnable.class.getName());
|
||||
assertThat(description.getLastExecution()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -96,6 +103,7 @@ class ScheduledTasksEndpointTests {
|
||||
assertThat(description.getInterval()).isOne();
|
||||
assertThat(description.getRunnable().getTarget())
|
||||
.isEqualTo(FixedDelayScheduledMethod.class.getName() + ".fixedDelay");
|
||||
assertThat(description.getLastExecution()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -110,6 +118,7 @@ class ScheduledTasksEndpointTests {
|
||||
assertThat(description.getInitialDelay()).isEqualTo(2000);
|
||||
assertThat(description.getInterval()).isEqualTo(1000);
|
||||
assertThat(description.getRunnable().getTarget()).contains(FixedDelayTriggerRunnable.class.getName());
|
||||
assertThat(description.getLastExecution()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -124,6 +133,7 @@ class ScheduledTasksEndpointTests {
|
||||
assertThat(description.getInitialDelay()).isEqualTo(0);
|
||||
assertThat(description.getInterval()).isEqualTo(1000);
|
||||
assertThat(description.getRunnable().getTarget()).contains(FixedDelayTriggerRunnable.class.getName());
|
||||
assertThatTaskMayHaveBeenExecuted(description);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -139,6 +149,7 @@ class ScheduledTasksEndpointTests {
|
||||
assertThat(description.getInterval()).isEqualTo(3);
|
||||
assertThat(description.getRunnable().getTarget())
|
||||
.isEqualTo(FixedRateScheduledMethod.class.getName() + ".fixedRate");
|
||||
assertThat(description.getLastExecution()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -153,6 +164,7 @@ class ScheduledTasksEndpointTests {
|
||||
assertThat(description.getInitialDelay()).isEqualTo(3000);
|
||||
assertThat(description.getInterval()).isEqualTo(2000);
|
||||
assertThat(description.getRunnable().getTarget()).contains(FixedRateTriggerRunnable.class.getName());
|
||||
assertThat(description.getLastExecution()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -167,6 +179,7 @@ class ScheduledTasksEndpointTests {
|
||||
assertThat(description.getInitialDelay()).isEqualTo(0);
|
||||
assertThat(description.getInterval()).isEqualTo(2000);
|
||||
assertThat(description.getRunnable().getTarget()).contains(FixedRateTriggerRunnable.class.getName());
|
||||
assertThatTaskMayHaveBeenExecuted(description);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -180,6 +193,7 @@ class ScheduledTasksEndpointTests {
|
||||
CustomTriggerTaskDescriptor description = (CustomTriggerTaskDescriptor) tasks.getCustom().get(0);
|
||||
assertThat(description.getRunnable().getTarget()).contains(CustomTriggerRunnable.class.getName());
|
||||
assertThat(description.getTrigger()).isEqualTo(CustomTriggerTask.trigger.toString());
|
||||
assertThatTaskMayHaveBeenExecuted(description);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -197,6 +211,16 @@ class ScheduledTasksEndpointTests {
|
||||
}
|
||||
}
|
||||
|
||||
private void assertThatTaskMayHaveBeenExecuted(TaskDescriptor descriptor) {
|
||||
LastExecution lastExecution = descriptor.getLastExecution();
|
||||
if (lastExecution != null) {
|
||||
if (lastExecution.getStatus() == Status.SUCCESS) {
|
||||
assertThat(lastExecution.getTime()).isInThePast();
|
||||
assertThat(lastExecution.getException()).isNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void run(Class<?> configuration, Consumer<ScheduledTasksDescriptor> consumer) {
|
||||
this.contextRunner.withUserConfiguration(configuration)
|
||||
.run((context) -> consumer.accept(context.getBean(ScheduledTasksEndpoint.class).scheduledTasks()));
|
||||
|
||||
Reference in New Issue
Block a user