Support for one-time tasks with just @Scheduled(initialDelay=...)
Closes gh-31211
This commit is contained in:
@@ -346,7 +346,7 @@ For example, the previous example can also be written as follows.
|
||||
|
||||
If you need a fixed-rate execution, you can use the `fixedRate` attribute within the
|
||||
annotation. The following method is invoked every five seconds (measured between the
|
||||
successive start times of each invocation).
|
||||
successive start times of each invocation):
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -356,9 +356,9 @@ successive start times of each invocation).
|
||||
}
|
||||
----
|
||||
|
||||
For fixed-delay and fixed-rate tasks, you can specify an initial delay by indicating the
|
||||
amount of time to wait before the first execution of the method, as the following
|
||||
`fixedRate` example shows.
|
||||
For fixed-delay and fixed-rate tasks, you can specify an initial delay by indicating
|
||||
the amount of time to wait before the first execution of the method, as the following
|
||||
`fixedRate` example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -368,6 +368,17 @@ amount of time to wait before the first execution of the method, as the followin
|
||||
}
|
||||
----
|
||||
|
||||
For one-time tasks, you can just specify an initial delay by indicating the amount
|
||||
of time to wait before the intended execution of the method:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Scheduled(initialDelay = 1000)
|
||||
public void doSomething() {
|
||||
// something that should run only once
|
||||
}
|
||||
----
|
||||
|
||||
If simple periodic scheduling is not expressive enough, you can provide a
|
||||
xref:integration/scheduling.adoc#scheduling-cron-expression[cron expression].
|
||||
The following example runs only on weekdays:
|
||||
|
||||
@@ -28,9 +28,10 @@ import org.springframework.aot.hint.annotation.Reflective;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
|
||||
/**
|
||||
* Annotation that marks a method to be scheduled. Exactly one of the
|
||||
* {@link #cron}, {@link #fixedDelay}, or {@link #fixedRate} attributes
|
||||
* must be specified.
|
||||
* Annotation that marks a method to be scheduled. For periodic tasks, exactly one
|
||||
* of the {@link #cron}, {@link #fixedDelay}, or {@link #fixedRate} attributes
|
||||
* must be specified, and additionally an optional {@link #initialDelay}.
|
||||
* For a one-time task, it is sufficient to just specify an {@link #initialDelay}.
|
||||
*
|
||||
* <p>The annotated method must not accept arguments. It will typically have
|
||||
* a {@code void} return type; if not, the returned value will be ignored
|
||||
|
||||
@@ -65,6 +65,7 @@ import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.config.CronTask;
|
||||
import org.springframework.scheduling.config.FixedDelayTask;
|
||||
import org.springframework.scheduling.config.FixedRateTask;
|
||||
import org.springframework.scheduling.config.OneTimeTask;
|
||||
import org.springframework.scheduling.config.ScheduledTask;
|
||||
import org.springframework.scheduling.config.ScheduledTaskHolder;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
@@ -393,8 +394,7 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
private void processScheduledTask(Scheduled scheduled, Runnable runnable, Method method, Object bean) {
|
||||
try {
|
||||
boolean processedSchedule = false;
|
||||
String errorMessage =
|
||||
"Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";
|
||||
String errorMessage = "Exactly one of the 'cron', 'fixedDelay' or 'fixedRate' attributes is required";
|
||||
|
||||
Set<ScheduledTask> tasks = new LinkedHashSet<>(4);
|
||||
|
||||
@@ -442,18 +442,15 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
}
|
||||
|
||||
// At this point we don't need to differentiate between initial delay set or not anymore
|
||||
if (initialDelay.isNegative()) {
|
||||
initialDelay = Duration.ZERO;
|
||||
}
|
||||
Duration delayToUse = (initialDelay.isNegative() ? Duration.ZERO : initialDelay);
|
||||
|
||||
// Check fixed delay
|
||||
Duration fixedDelay = toDuration(scheduled.fixedDelay(), scheduled.timeUnit());
|
||||
if (!fixedDelay.isNegative()) {
|
||||
Assert.isTrue(!processedSchedule, errorMessage);
|
||||
processedSchedule = true;
|
||||
tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
|
||||
tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, delayToUse)));
|
||||
}
|
||||
|
||||
String fixedDelayString = scheduled.fixedDelayString();
|
||||
if (StringUtils.hasText(fixedDelayString)) {
|
||||
if (this.embeddedValueResolver != null) {
|
||||
@@ -469,7 +466,7 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid fixedDelayString value \"" + fixedDelayString + "\" - cannot parse into long");
|
||||
}
|
||||
tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
|
||||
tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, delayToUse)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,7 +475,7 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
if (!fixedRate.isNegative()) {
|
||||
Assert.isTrue(!processedSchedule, errorMessage);
|
||||
processedSchedule = true;
|
||||
tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));
|
||||
tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, delayToUse)));
|
||||
}
|
||||
String fixedRateString = scheduled.fixedRateString();
|
||||
if (StringUtils.hasText(fixedRateString)) {
|
||||
@@ -495,12 +492,16 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid fixedRateString value \"" + fixedRateString + "\" - cannot parse into long");
|
||||
}
|
||||
tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));
|
||||
tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, delayToUse)));
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether we had any attribute set
|
||||
Assert.isTrue(processedSchedule, errorMessage);
|
||||
if (!processedSchedule) {
|
||||
if (initialDelay.isNegative()) {
|
||||
throw new IllegalArgumentException("One-time task only supported with specified initial delay");
|
||||
}
|
||||
tasks.add(this.registrar.scheduleOneTimeTask(new OneTimeTask(runnable, delayToUse)));
|
||||
}
|
||||
|
||||
// Finally register the scheduled tasks
|
||||
synchronized (this.scheduledTasks) {
|
||||
@@ -548,7 +549,13 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
}
|
||||
|
||||
private static Duration toDuration(long value, TimeUnit timeUnit) {
|
||||
return Duration.of(value, timeUnit.toChronoUnit());
|
||||
try {
|
||||
return Duration.of(value, timeUnit.toChronoUnit());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported unit " + timeUnit + " for value \"" + value + "\": " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static Duration toDuration(String value, TimeUnit timeUnit) {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.Duration;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Task} implementation defining a {@code Runnable} with an initial delay.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 6.1
|
||||
*/
|
||||
public class DelayedTask extends Task {
|
||||
|
||||
private final Duration initialDelay;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code DelayedTask}.
|
||||
* @param runnable the underlying task to execute
|
||||
* @param initialDelay the initial delay before execution of the task
|
||||
*/
|
||||
public DelayedTask(Runnable runnable, Duration initialDelay) {
|
||||
super(runnable);
|
||||
Assert.notNull(initialDelay, "InitialDelay must not be null");
|
||||
this.initialDelay = initialDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*/
|
||||
DelayedTask(DelayedTask task) {
|
||||
super(task.getRunnable());
|
||||
Assert.notNull(task, "DelayedTask must not be null");
|
||||
this.initialDelay = task.getInitialDelayDuration();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the initial delay before first execution of the task.
|
||||
*/
|
||||
public Duration getInitialDelayDuration() {
|
||||
return this.initialDelay;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -56,5 +56,4 @@ public class FixedRateTask extends IntervalTask {
|
||||
super(task);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -31,12 +31,10 @@ import org.springframework.util.Assert;
|
||||
* @see ScheduledTaskRegistrar#addFixedRateTask(IntervalTask)
|
||||
* @see ScheduledTaskRegistrar#addFixedDelayTask(IntervalTask)
|
||||
*/
|
||||
public class IntervalTask extends Task {
|
||||
public class IntervalTask extends DelayedTask {
|
||||
|
||||
private final Duration interval;
|
||||
|
||||
private final Duration initialDelay;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code IntervalTask}.
|
||||
@@ -79,28 +77,20 @@ public class IntervalTask extends Task {
|
||||
* @since 6.0
|
||||
*/
|
||||
public IntervalTask(Runnable runnable, Duration interval, Duration initialDelay) {
|
||||
super(runnable);
|
||||
super(runnable, initialDelay);
|
||||
Assert.notNull(interval, "Interval must not be null");
|
||||
Assert.notNull(initialDelay, "InitialDelay must not be null");
|
||||
|
||||
this.interval = interval;
|
||||
this.initialDelay = initialDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*/
|
||||
IntervalTask(IntervalTask task) {
|
||||
super(task.getRunnable());
|
||||
Assert.notNull(task, "IntervalTask must not be null");
|
||||
|
||||
super(task);
|
||||
this.interval = task.getIntervalDuration();
|
||||
this.initialDelay = task.getInitialDelayDuration();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return how often in milliseconds the task should be executed.
|
||||
* @deprecated as of 6.0, in favor of {@link #getIntervalDuration()}
|
||||
@@ -124,15 +114,16 @@ public class IntervalTask extends Task {
|
||||
*/
|
||||
@Deprecated(since = "6.0")
|
||||
public long getInitialDelay() {
|
||||
return this.initialDelay.toMillis();
|
||||
return getInitialDelayDuration().toMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the initial delay before first execution of the task.
|
||||
* @since 6.0
|
||||
*/
|
||||
@Override
|
||||
public Duration getInitialDelayDuration() {
|
||||
return this.initialDelay;
|
||||
return super.getInitialDelayDuration();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.Duration;
|
||||
|
||||
/**
|
||||
* {@link Task} implementation defining a {@code Runnable} with an initial delay.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 6.1
|
||||
* @see ScheduledTaskRegistrar#addOneTimeTask(DelayedTask)
|
||||
*/
|
||||
public class OneTimeTask extends DelayedTask {
|
||||
|
||||
/**
|
||||
* Create a new {@code DelayedTask}.
|
||||
* @param runnable the underlying task to execute
|
||||
* @param initialDelay the initial delay before execution of the task
|
||||
*/
|
||||
public OneTimeTask(Runnable runnable, Duration initialDelay) {
|
||||
super(runnable, initialDelay);
|
||||
}
|
||||
|
||||
OneTimeTask(DelayedTask task) {
|
||||
super(task);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -95,6 +95,9 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
|
||||
@Nullable
|
||||
private List<IntervalTask> fixedDelayTasks;
|
||||
|
||||
@Nullable
|
||||
private List<DelayedTask> oneTimeTasks;
|
||||
|
||||
private final Map<Task, ScheduledTask> unresolvedTasks = new HashMap<>(16);
|
||||
|
||||
private final Set<ScheduledTask> scheduledTasks = new LinkedHashSet<>(16);
|
||||
@@ -136,6 +139,14 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
|
||||
return this.taskScheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure an {@link ObservationRegistry} to record observations for scheduled tasks.
|
||||
* @since 6.1
|
||||
*/
|
||||
public void setObservationRegistry(@Nullable ObservationRegistry observationRegistry) {
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link ObservationRegistry} for this registrar.
|
||||
* @since 6.1
|
||||
@@ -145,14 +156,6 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
|
||||
return this.observationRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure an {@link ObservationRegistry} to record observations for scheduled tasks.
|
||||
* @since 6.1
|
||||
*/
|
||||
public void setObservationRegistry(@Nullable ObservationRegistry observationRegistry) {
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify triggered tasks as a Map of Runnables (the tasks) and Trigger objects
|
||||
* (typically custom implementations of the {@link Trigger} interface).
|
||||
@@ -318,7 +321,7 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
|
||||
*/
|
||||
@Deprecated(since = "6.0")
|
||||
public void addFixedRateTask(Runnable task, long interval) {
|
||||
addFixedRateTask(new IntervalTask(task, Duration.ofMillis(interval)));
|
||||
addFixedRateTask(new IntervalTask(task, interval));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,6 +337,7 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
|
||||
* Add a fixed-rate {@link IntervalTask}.
|
||||
* @since 3.2
|
||||
* @see TaskScheduler#scheduleAtFixedRate(Runnable, Duration)
|
||||
* @see FixedRateTask
|
||||
*/
|
||||
public void addFixedRateTask(IntervalTask task) {
|
||||
if (this.fixedRateTasks == null) {
|
||||
@@ -347,8 +351,8 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
|
||||
* @deprecated as of 6.0, in favor of {@link #addFixedDelayTask(Runnable, Duration)}
|
||||
*/
|
||||
@Deprecated(since = "6.0")
|
||||
public void addFixedDelayTask(Runnable task, long delay) {
|
||||
addFixedDelayTask(new IntervalTask(task, Duration.ofMillis(delay)));
|
||||
public void addFixedDelayTask(Runnable task, long interval) {
|
||||
addFixedDelayTask(new IntervalTask(task, interval));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,14 +360,15 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
|
||||
* @since 6.0
|
||||
* @see TaskScheduler#scheduleWithFixedDelay(Runnable, Duration)
|
||||
*/
|
||||
public void addFixedDelayTask(Runnable task, Duration delay) {
|
||||
addFixedDelayTask(new IntervalTask(task, delay));
|
||||
public void addFixedDelayTask(Runnable task, Duration interval) {
|
||||
addFixedDelayTask(new IntervalTask(task, interval));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a fixed-delay {@link IntervalTask}.
|
||||
* @since 3.2
|
||||
* @see TaskScheduler#scheduleWithFixedDelay(Runnable, Duration)
|
||||
* @see FixedDelayTask
|
||||
*/
|
||||
public void addFixedDelayTask(IntervalTask task) {
|
||||
if (this.fixedDelayTasks == null) {
|
||||
@@ -372,6 +377,28 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
|
||||
this.fixedDelayTasks.add(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Runnable task to be triggered once after the given initial delay.
|
||||
* @since 6.1
|
||||
* @see TaskScheduler#schedule(Runnable, Instant)
|
||||
*/
|
||||
public void addOneTimeTask(Runnable task, Duration initialDelay) {
|
||||
addOneTimeTask(new OneTimeTask(task, initialDelay));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a one-time {@link DelayedTask}.
|
||||
* @since 6.1
|
||||
* @see TaskScheduler#schedule(Runnable, Instant)
|
||||
* @see OneTimeTask
|
||||
*/
|
||||
public void addOneTimeTask(DelayedTask task) {
|
||||
if (this.oneTimeTasks == null) {
|
||||
this.oneTimeTasks = new ArrayList<>();
|
||||
}
|
||||
this.oneTimeTasks.add(task);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return whether this {@code ScheduledTaskRegistrar} has any tasks registered.
|
||||
@@ -381,7 +408,8 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
|
||||
return (!CollectionUtils.isEmpty(this.triggerTasks) ||
|
||||
!CollectionUtils.isEmpty(this.cronTasks) ||
|
||||
!CollectionUtils.isEmpty(this.fixedRateTasks) ||
|
||||
!CollectionUtils.isEmpty(this.fixedDelayTasks));
|
||||
!CollectionUtils.isEmpty(this.fixedDelayTasks) ||
|
||||
!CollectionUtils.isEmpty(this.oneTimeTasks));
|
||||
}
|
||||
|
||||
|
||||
@@ -432,6 +460,16 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.oneTimeTasks != null) {
|
||||
for (DelayedTask task : this.oneTimeTasks) {
|
||||
if (task instanceof OneTimeTask oneTimeTask) {
|
||||
addScheduledTask(scheduleOneTimeTask(oneTimeTask));
|
||||
}
|
||||
else {
|
||||
addScheduledTask(scheduleOneTimeTask(new OneTimeTask(task)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addScheduledTask(@Nullable ScheduledTask task) {
|
||||
@@ -558,6 +596,32 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
|
||||
return (newTask ? scheduledTask : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the specified one-time task, either right away if possible
|
||||
* or on initialization of the scheduler.
|
||||
* @return a handle to the scheduled task, allowing to cancel it
|
||||
* (or {@code null} if processing a previously registered task)
|
||||
* @since 6.1
|
||||
*/
|
||||
@Nullable
|
||||
public ScheduledTask scheduleOneTimeTask(OneTimeTask task) {
|
||||
ScheduledTask scheduledTask = this.unresolvedTasks.remove(task);
|
||||
boolean newTask = false;
|
||||
if (scheduledTask == null) {
|
||||
scheduledTask = new ScheduledTask(task);
|
||||
newTask = true;
|
||||
}
|
||||
if (this.taskScheduler != null) {
|
||||
Instant startTime = this.taskScheduler.getClock().instant().plus(task.getInitialDelayDuration());
|
||||
scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), startTime);
|
||||
}
|
||||
else {
|
||||
addOneTimeTask(task);
|
||||
this.unresolvedTasks.put(task, scheduledTask);
|
||||
}
|
||||
return (newTask ? scheduledTask : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return all locally registered tasks that have been scheduled by this registrar.
|
||||
|
||||
@@ -218,15 +218,6 @@ public class EnableSchedulingTests {
|
||||
assertThat(ctx.getBean(ThreadAwareWorker.class).executedByThread).startsWith("taskScheduler-");
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledForTestGroups(LONG_RUNNING)
|
||||
public void withTriggerTask() throws InterruptedException {
|
||||
ctx = new AnnotationConfigApplicationContext(TriggerTaskConfig.class);
|
||||
|
||||
Thread.sleep(110);
|
||||
assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThan(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledForTestGroups(LONG_RUNNING)
|
||||
public void withInitiallyDelayedFixedRateTask() throws InterruptedException {
|
||||
@@ -235,9 +226,30 @@ public class EnableSchedulingTests {
|
||||
Thread.sleep(1950);
|
||||
AtomicInteger counter = ctx.getBean(AtomicInteger.class);
|
||||
|
||||
// The @Scheduled method should have been called at least once but
|
||||
// not more times than the delay allows.
|
||||
assertThat(counter.get()).isBetween(1, 10);
|
||||
// The @Scheduled method should have been called several times
|
||||
// but not more times than the delay allows.
|
||||
assertThat(counter.get()).isBetween(2, 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledForTestGroups(LONG_RUNNING)
|
||||
public void withOneTimeTask() throws InterruptedException {
|
||||
ctx = new AnnotationConfigApplicationContext(OneTimeTaskConfig.class);
|
||||
|
||||
Thread.sleep(110);
|
||||
AtomicInteger counter = ctx.getBean(AtomicInteger.class);
|
||||
|
||||
// The @Scheduled method should have been called exactly once.
|
||||
assertThat(counter.get()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledForTestGroups(LONG_RUNNING)
|
||||
public void withTriggerTask() throws InterruptedException {
|
||||
ctx = new AnnotationConfigApplicationContext(TriggerTaskConfig.class);
|
||||
|
||||
Thread.sleep(110);
|
||||
assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThan(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -598,6 +610,38 @@ public class EnableSchedulingTests {
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
static class FixedRateTaskConfig_withInitialDelay {
|
||||
|
||||
@Bean
|
||||
public AtomicInteger counter() {
|
||||
return new AtomicInteger();
|
||||
}
|
||||
|
||||
@Scheduled(initialDelay = 1000, fixedRate = 100)
|
||||
public void task() {
|
||||
counter().incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
static class OneTimeTaskConfig {
|
||||
|
||||
@Bean
|
||||
public AtomicInteger counter() {
|
||||
return new AtomicInteger();
|
||||
}
|
||||
|
||||
@Scheduled(initialDelay = 10)
|
||||
public void task() {
|
||||
counter().incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class TriggerTaskConfig {
|
||||
|
||||
@@ -616,20 +660,4 @@ public class EnableSchedulingTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
static class FixedRateTaskConfig_withInitialDelay {
|
||||
|
||||
@Bean
|
||||
public AtomicInteger counter() {
|
||||
return new AtomicInteger();
|
||||
}
|
||||
|
||||
@Scheduled(initialDelay = 1000, fixedRate = 100)
|
||||
public void task() {
|
||||
counter().incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.TriggerContext;
|
||||
import org.springframework.scheduling.config.CronTask;
|
||||
import org.springframework.scheduling.config.IntervalTask;
|
||||
import org.springframework.scheduling.config.OneTimeTask;
|
||||
import org.springframework.scheduling.config.ScheduledTaskHolder;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
import org.springframework.scheduling.support.CronTrigger;
|
||||
@@ -266,6 +267,33 @@ class ScheduledAnnotationBeanPostProcessorTests {
|
||||
assertThat(task2.getIntervalDuration()).isEqualTo(Duration.ofMillis(4_000L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void oneTimeTask() {
|
||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||
BeanDefinition targetDefinition = new RootBeanDefinition(OneTimeTaskBean.class);
|
||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||
context.registerBeanDefinition("target", targetDefinition);
|
||||
context.refresh();
|
||||
|
||||
ScheduledTaskHolder postProcessor = context.getBean("postProcessor", ScheduledTaskHolder.class);
|
||||
assertThat(postProcessor.getScheduledTasks()).hasSize(1);
|
||||
|
||||
Object target = context.getBean("target");
|
||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||
new DirectFieldAccessor(postProcessor).getPropertyValue("registrar");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<OneTimeTask> oneTimeTasks = (List<OneTimeTask>)
|
||||
new DirectFieldAccessor(registrar).getPropertyValue("oneTimeTasks");
|
||||
assertThat(oneTimeTasks).hasSize(1);
|
||||
OneTimeTask task = oneTimeTasks.get(0);
|
||||
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
|
||||
Object targetObject = runnable.getTarget();
|
||||
Method targetMethod = runnable.getMethod();
|
||||
assertThat(targetObject).isEqualTo(target);
|
||||
assertThat(targetMethod.getName()).isEqualTo("oneTimeTask");
|
||||
assertThat(task.getInitialDelayDuration()).isEqualTo(Duration.ofMillis(2_000L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void cronTask() {
|
||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||
@@ -848,6 +876,14 @@ class ScheduledAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
static class OneTimeTaskBean {
|
||||
|
||||
@Scheduled(initialDelay = 2_000)
|
||||
private void oneTimeTask() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Validated
|
||||
static class CronTestBean {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user