Add execution metadata to tasks and scheduled tasks
This commit adds new information about the execution and scheduling of tasks. The `Task` type now exposes the `TaskExecutionOutcome` of the latest execution; this includes the instant the execution started, the execution outcome and any thrown exception. The `ScheduledTask` contract can now provide the time when the next execution is scheduled. Closes gh-24560
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.springframework.scheduling.config;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -25,6 +27,7 @@ import org.springframework.lang.Nullable;
|
||||
* used as a return value for scheduling methods.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Brian Clozel
|
||||
* @since 4.3
|
||||
* @see ScheduledTaskRegistrar#scheduleCronTask(CronTask)
|
||||
* @see ScheduledTaskRegistrar#scheduleFixedRateTask(FixedRateTask)
|
||||
@@ -76,6 +79,22 @@ public final class ScheduledTask {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next scheduled execution of the task, or {@code null}
|
||||
* if the task has been cancelled or no new execution is scheduled.
|
||||
* @since 6.2
|
||||
*/
|
||||
@Nullable
|
||||
public Instant nextExecution() {
|
||||
if (this.future != null && !this.future.isCancelled()) {
|
||||
long delay = this.future.getDelay(TimeUnit.MILLISECONDS);
|
||||
if (delay > 0) {
|
||||
return Instant.now().plusMillis(delay);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.task.toString();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.scheduling.config;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -24,12 +26,15 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Brian Clozel
|
||||
* @since 3.2
|
||||
*/
|
||||
public class Task {
|
||||
|
||||
private final Runnable runnable;
|
||||
|
||||
private TaskExecutionOutcome lastExecutionOutcome;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code Task}.
|
||||
@@ -37,7 +42,8 @@ public class Task {
|
||||
*/
|
||||
public Task(Runnable runnable) {
|
||||
Assert.notNull(runnable, "Runnable must not be null");
|
||||
this.runnable = runnable;
|
||||
this.runnable = new OutcomeTrackingRunnable(runnable);
|
||||
this.lastExecutionOutcome = TaskExecutionOutcome.create();
|
||||
}
|
||||
|
||||
|
||||
@@ -48,10 +54,45 @@ public class Task {
|
||||
return this.runnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the outcome of the last task execution.
|
||||
* @since 6.2
|
||||
*/
|
||||
public TaskExecutionOutcome getLastExecutionOutcome() {
|
||||
return this.lastExecutionOutcome;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.runnable.toString();
|
||||
}
|
||||
|
||||
|
||||
private class OutcomeTrackingRunnable implements Runnable {
|
||||
|
||||
private final Runnable runnable;
|
||||
|
||||
public OutcomeTrackingRunnable(Runnable runnable) {
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Task.this.lastExecutionOutcome = Task.this.lastExecutionOutcome.start(Instant.now());
|
||||
this.runnable.run();
|
||||
Task.this.lastExecutionOutcome = Task.this.lastExecutionOutcome.success();
|
||||
}
|
||||
catch (Throwable exc) {
|
||||
Task.this.lastExecutionOutcome = Task.this.lastExecutionOutcome.failure(exc);
|
||||
throw exc;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.runnable.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.config;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Outcome of a {@link Task} execution.
|
||||
* @param executionTime the instant when the task execution started, {@code null} if the task has not started.
|
||||
* @param status the {@link Status} of the execution outcome.
|
||||
* @param throwable the exception thrown from the task execution, if any.
|
||||
* @author Brian Clozel
|
||||
* @since 6.2
|
||||
*/
|
||||
public record TaskExecutionOutcome(@Nullable Instant executionTime, Status status, @Nullable Throwable throwable) {
|
||||
|
||||
TaskExecutionOutcome start(Instant executionTime) {
|
||||
return new TaskExecutionOutcome(executionTime, Status.STARTED, null);
|
||||
}
|
||||
|
||||
TaskExecutionOutcome success() {
|
||||
Assert.state(this.executionTime != null, "Task has not been started yet");
|
||||
return new TaskExecutionOutcome(this.executionTime, Status.SUCCESS, null);
|
||||
}
|
||||
|
||||
TaskExecutionOutcome failure(Throwable throwable) {
|
||||
Assert.state(this.executionTime != null, "Task has not been started yet");
|
||||
return new TaskExecutionOutcome(this.executionTime, Status.ERROR, throwable);
|
||||
}
|
||||
|
||||
static TaskExecutionOutcome create() {
|
||||
return new TaskExecutionOutcome(null, Status.NONE, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Status of the task execution outcome.
|
||||
*/
|
||||
public enum Status {
|
||||
/**
|
||||
* The task has not been executed so far.
|
||||
*/
|
||||
NONE,
|
||||
/**
|
||||
* The task execution has been started and is ongoing.
|
||||
*/
|
||||
STARTED,
|
||||
/**
|
||||
* The task execution finished successfully.
|
||||
*/
|
||||
SUCCESS,
|
||||
/**
|
||||
* The task execution finished with an error.
|
||||
*/
|
||||
ERROR
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user