Update scheduling package to use java.time

This commit deprecates all methods in org.springframework.scheduling
that use

- Date, in favor of variants that take an Instant.
- long & TimeUnit, in favor of variants that take a Duration.

Closes: gh-28714
This commit is contained in:
Arjen Poutsma 2022-07-05 10:32:47 +02:00
parent 8ccf05adee
commit 9b739a2310
38 changed files with 791 additions and 412 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -89,11 +89,8 @@ public interface TaskScheduler {
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress) * for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
* @since 5.0 * @since 5.0
* @see #schedule(Runnable, Date)
*/ */
default ScheduledFuture<?> schedule(Runnable task, Instant startTime) { ScheduledFuture<?> schedule(Runnable task, Instant startTime);
return schedule(task, Date.from(startTime));
}
/** /**
* Schedule the given {@link Runnable}, invoking it at the specified execution time. * Schedule the given {@link Runnable}, invoking it at the specified execution time.
@ -105,8 +102,12 @@ public interface TaskScheduler {
* @return a {@link ScheduledFuture} representing pending completion of the task * @return a {@link ScheduledFuture} representing pending completion of the task
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress) * for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
* @deprecated as of 6.0, in favor of {@link #schedule(Runnable, Instant)}
*/ */
ScheduledFuture<?> schedule(Runnable task, Date startTime); @Deprecated
default ScheduledFuture<?> schedule(Runnable task, Date startTime) {
return schedule(task, startTime.toInstant());
}
/** /**
* Schedule the given {@link Runnable}, invoking it at the specified execution time * Schedule the given {@link Runnable}, invoking it at the specified execution time
@ -121,11 +122,8 @@ public interface TaskScheduler {
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress) * for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
* @since 5.0 * @since 5.0
* @see #scheduleAtFixedRate(Runnable, Date, long)
*/ */
default ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) { ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime, Duration period);
return scheduleAtFixedRate(task, Date.from(startTime), period.toMillis());
}
/** /**
* Schedule the given {@link Runnable}, invoking it at the specified execution time * Schedule the given {@link Runnable}, invoking it at the specified execution time
@ -139,8 +137,12 @@ public interface TaskScheduler {
* @return a {@link ScheduledFuture} representing pending completion of the task * @return a {@link ScheduledFuture} representing pending completion of the task
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress) * for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
* @deprecated as of 6.0, in favor of {@link #scheduleAtFixedRate(Runnable, Instant, Duration)}
*/ */
ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period); @Deprecated
default ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) {
return scheduleAtFixedRate(task, startTime.toInstant(), Duration.ofMillis(period));
}
/** /**
* Schedule the given {@link Runnable}, starting as soon as possible and * Schedule the given {@link Runnable}, starting as soon as possible and
@ -153,11 +155,8 @@ public interface TaskScheduler {
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress) * for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
* @since 5.0 * @since 5.0
* @see #scheduleAtFixedRate(Runnable, long)
*/ */
default ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period) { ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period);
return scheduleAtFixedRate(task, period.toMillis());
}
/** /**
* Schedule the given {@link Runnable}, starting as soon as possible and * Schedule the given {@link Runnable}, starting as soon as possible and
@ -169,8 +168,12 @@ public interface TaskScheduler {
* @return a {@link ScheduledFuture} representing pending completion of the task * @return a {@link ScheduledFuture} representing pending completion of the task
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress) * for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
* @deprecated as of 6.0, in favor of {@link #scheduleAtFixedRate(Runnable, Duration)}
*/ */
ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period); @Deprecated
default ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) {
return scheduleAtFixedRate(task, Duration.ofMillis(period));
}
/** /**
* Schedule the given {@link Runnable}, invoking it at the specified execution time * Schedule the given {@link Runnable}, invoking it at the specified execution time
@ -186,11 +189,8 @@ public interface TaskScheduler {
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress) * for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
* @since 5.0 * @since 5.0
* @see #scheduleWithFixedDelay(Runnable, Date, long)
*/ */
default ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) { ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);
return scheduleWithFixedDelay(task, Date.from(startTime), delay.toMillis());
}
/** /**
* Schedule the given {@link Runnable}, invoking it at the specified execution time * Schedule the given {@link Runnable}, invoking it at the specified execution time
@ -206,8 +206,12 @@ public interface TaskScheduler {
* @return a {@link ScheduledFuture} representing pending completion of the task * @return a {@link ScheduledFuture} representing pending completion of the task
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress) * for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
* @deprecated as of 6.0, in favor of {@link #scheduleWithFixedDelay(Runnable, Instant, Duration)}
*/ */
ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay); @Deprecated
default ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay) {
return scheduleWithFixedDelay(task, startTime.toInstant(), Duration.ofMillis(delay));
}
/** /**
* Schedule the given {@link Runnable}, starting as soon as possible and invoking it with * Schedule the given {@link Runnable}, starting as soon as possible and invoking it with
@ -220,11 +224,8 @@ public interface TaskScheduler {
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress) * for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
* @since 5.0 * @since 5.0
* @see #scheduleWithFixedDelay(Runnable, long)
*/ */
default ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay) { ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay);
return scheduleWithFixedDelay(task, delay.toMillis());
}
/** /**
* Schedule the given {@link Runnable}, starting as soon as possible and invoking it with * Schedule the given {@link Runnable}, starting as soon as possible and invoking it with
@ -237,7 +238,11 @@ public interface TaskScheduler {
* @return a {@link ScheduledFuture} representing pending completion of the task * @return a {@link ScheduledFuture} representing pending completion of the task
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress) * for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
* @deprecated as of 6.0, in favor of {@link #scheduleWithFixedDelay(Runnable, Duration)}
*/ */
ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay); @Deprecated
default ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay) {
return scheduleWithFixedDelay(task, Duration.ofMillis(delay));
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
package org.springframework.scheduling; package org.springframework.scheduling;
import java.time.Instant;
import java.util.Date; import java.util.Date;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -37,8 +38,24 @@ public interface Trigger {
* and last completion time * and last completion time
* @return the next execution time as defined by the trigger, * @return the next execution time as defined by the trigger,
* or {@code null} if the trigger won't fire anymore * or {@code null} if the trigger won't fire anymore
* @deprecated as of 6.0, in favor of {@link #nextExecution(TriggerContext)}
*/
@Deprecated
@Nullable
default Date nextExecutionTime(TriggerContext triggerContext) {
Instant instant = nextExecution(triggerContext);
return instant != null ? Date.from(instant) : null;
}
/**
* Determine the next execution time according to the given trigger context.
* @param triggerContext context object encapsulating last execution times
* and last completion time
* @return the next execution time as defined by the trigger,
* or {@code null} if the trigger won't fire anymore
* @since 6.0
*/ */
@Nullable @Nullable
Date nextExecutionTime(TriggerContext triggerContext); Instant nextExecution(TriggerContext triggerContext);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package org.springframework.scheduling; package org.springframework.scheduling;
import java.time.Clock; import java.time.Clock;
import java.time.Instant;
import java.util.Date; import java.util.Date;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -43,22 +44,59 @@ public interface TriggerContext {
/** /**
* Return the last <i>scheduled</i> execution time of the task, * Return the last <i>scheduled</i> execution time of the task,
* or {@code null} if not scheduled before. * or {@code null} if not scheduled before.
* @deprecated as of 6.0, in favor on {@link #lastScheduledExecution()}
*/ */
@Nullable @Nullable
Date lastScheduledExecutionTime(); @Deprecated
default Date lastScheduledExecutionTime() {
Instant instant = lastScheduledExecution();
return instant != null ? Date.from(instant) : null;
}
/**
* Return the last <i>scheduled</i> execution time of the task,
* or {@code null} if not scheduled before.
* @since 6.0
*/
@Nullable
Instant lastScheduledExecution();
/**
* Return the last <i>actual</i> execution time of the task,
* or {@code null} if not scheduled before.
* @deprecated as of 6.0, in favor on {@link #lastActualExecution()}
*/
@Deprecated
@Nullable
default Date lastActualExecutionTime() {
Instant instant = lastActualExecution();
return instant != null ? Date.from(instant) : null;
}
/** /**
* Return the last <i>actual</i> execution time of the task, * Return the last <i>actual</i> execution time of the task,
* or {@code null} if not scheduled before. * or {@code null} if not scheduled before.
*/ */
@Nullable @Nullable
Date lastActualExecutionTime(); Instant lastActualExecution();
/**
* Return the last completion time of the task,
* or {@code null} if not scheduled before.
* @deprecated as of 6.0, in favor on {@link #lastCompletion()}
*/
@Deprecated
@Nullable
default Date lastCompletionTime() {
Instant instant = lastCompletion();
return instant != null ? Date.from(instant) : null;
}
/** /**
* Return the last completion time of the task, * Return the last completion time of the task,
* or {@code null} if not scheduled before. * or {@code null} if not scheduled before.
*/ */
@Nullable @Nullable
Date lastCompletionTime(); Instant lastCompletion();
} }

View File

@ -405,16 +405,16 @@ public class ScheduledAnnotationBeanPostProcessor
Set<ScheduledTask> tasks = new LinkedHashSet<>(4); Set<ScheduledTask> tasks = new LinkedHashSet<>(4);
// Determine initial delay // Determine initial delay
long initialDelay = convertToMillis(scheduled.initialDelay(), scheduled.timeUnit()); Duration initialDelay = toDuration(scheduled.initialDelay(), scheduled.timeUnit());
String initialDelayString = scheduled.initialDelayString(); String initialDelayString = scheduled.initialDelayString();
if (StringUtils.hasText(initialDelayString)) { if (StringUtils.hasText(initialDelayString)) {
Assert.isTrue(initialDelay < 0, "Specify 'initialDelay' or 'initialDelayString', not both"); Assert.isTrue(initialDelay.isNegative(), "Specify 'initialDelay' or 'initialDelayString', not both");
if (this.embeddedValueResolver != null) { if (this.embeddedValueResolver != null) {
initialDelayString = this.embeddedValueResolver.resolveStringValue(initialDelayString); initialDelayString = this.embeddedValueResolver.resolveStringValue(initialDelayString);
} }
if (StringUtils.hasLength(initialDelayString)) { if (StringUtils.hasLength(initialDelayString)) {
try { try {
initialDelay = convertToMillis(initialDelayString, scheduled.timeUnit()); initialDelay = toDuration(initialDelayString, scheduled.timeUnit());
} }
catch (RuntimeException ex) { catch (RuntimeException ex) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
@ -432,7 +432,7 @@ public class ScheduledAnnotationBeanPostProcessor
zone = this.embeddedValueResolver.resolveStringValue(zone); zone = this.embeddedValueResolver.resolveStringValue(zone);
} }
if (StringUtils.hasLength(cron)) { if (StringUtils.hasLength(cron)) {
Assert.isTrue(initialDelay == -1, "'initialDelay' not supported for cron triggers"); Assert.isTrue(initialDelay.isNegative(), "'initialDelay' not supported for cron triggers");
processedSchedule = true; processedSchedule = true;
if (!Scheduled.CRON_DISABLED.equals(cron)) { if (!Scheduled.CRON_DISABLED.equals(cron)) {
TimeZone timeZone; TimeZone timeZone;
@ -448,13 +448,13 @@ public class ScheduledAnnotationBeanPostProcessor
} }
// At this point we don't need to differentiate between initial delay set or not anymore // At this point we don't need to differentiate between initial delay set or not anymore
if (initialDelay < 0) { if (initialDelay.isNegative()) {
initialDelay = 0; initialDelay = Duration.ZERO;
} }
// Check fixed delay // Check fixed delay
long fixedDelay = convertToMillis(scheduled.fixedDelay(), scheduled.timeUnit()); Duration fixedDelay = toDuration(scheduled.fixedDelay(), scheduled.timeUnit());
if (fixedDelay >= 0) { if (!fixedDelay.isNegative()) {
Assert.isTrue(!processedSchedule, errorMessage); Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true; processedSchedule = true;
tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay))); tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
@ -469,7 +469,7 @@ public class ScheduledAnnotationBeanPostProcessor
Assert.isTrue(!processedSchedule, errorMessage); Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true; processedSchedule = true;
try { try {
fixedDelay = convertToMillis(fixedDelayString, scheduled.timeUnit()); fixedDelay = toDuration(fixedDelayString, scheduled.timeUnit());
} }
catch (RuntimeException ex) { catch (RuntimeException ex) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
@ -480,8 +480,8 @@ public class ScheduledAnnotationBeanPostProcessor
} }
// Check fixed rate // Check fixed rate
long fixedRate = convertToMillis(scheduled.fixedRate(), scheduled.timeUnit()); Duration fixedRate = toDuration(scheduled.fixedRate(), scheduled.timeUnit());
if (fixedRate >= 0) { if (!fixedRate.isNegative()) {
Assert.isTrue(!processedSchedule, errorMessage); Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true; processedSchedule = true;
tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay))); tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));
@ -495,7 +495,7 @@ public class ScheduledAnnotationBeanPostProcessor
Assert.isTrue(!processedSchedule, errorMessage); Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true; processedSchedule = true;
try { try {
fixedRate = convertToMillis(fixedRateString, scheduled.timeUnit()); fixedRate = toDuration(fixedRateString, scheduled.timeUnit());
} }
catch (RuntimeException ex) { catch (RuntimeException ex) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
@ -535,15 +535,15 @@ public class ScheduledAnnotationBeanPostProcessor
return new ScheduledMethodRunnable(target, invocableMethod); return new ScheduledMethodRunnable(target, invocableMethod);
} }
private static long convertToMillis(long value, TimeUnit timeUnit) { private static Duration toDuration(long value, TimeUnit timeUnit) {
return TimeUnit.MILLISECONDS.convert(value, timeUnit); return Duration.of(value, timeUnit.toChronoUnit());
} }
private static long convertToMillis(String value, TimeUnit timeUnit) { private static Duration toDuration(String value, TimeUnit timeUnit) {
if (isDurationString(value)) { if (isDurationString(value)) {
return Duration.parse(value).toMillis(); return Duration.parse(value);
} }
return convertToMillis(Long.parseLong(value), timeUnit); return toDuration(Long.parseLong(value), timeUnit);
} }
private static boolean isDurationString(String value) { private static boolean isDurationString(String value) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,6 +17,8 @@
package org.springframework.scheduling.concurrent; package org.springframework.scheduling.concurrent;
import java.time.Clock; import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.Date; import java.util.Date;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -32,7 +34,7 @@ import org.springframework.core.task.TaskRejectedException;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger; import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.support.SimpleTriggerContext; import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.support.TaskUtils; import org.springframework.scheduling.support.TaskUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
@ -58,6 +60,7 @@ import org.springframework.util.ErrorHandler;
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Mark Fisher * @author Mark Fisher
* @author Arjen Poutsma
* @since 3.0 * @since 3.0
* @see java.util.concurrent.ScheduledExecutorService * @see java.util.concurrent.ScheduledExecutorService
* @see java.util.concurrent.ScheduledThreadPoolExecutor * @see java.util.concurrent.ScheduledThreadPoolExecutor
@ -206,10 +209,10 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
} }
@Override @Override
public ScheduledFuture<?> schedule(Runnable task, Date startTime) { public ScheduledFuture<?> schedule(Runnable task, Instant startTime) {
long initialDelay = startTime.getTime() - this.clock.millis(); Duration initialDelay = Duration.between(this.clock.instant(), startTime);
try { try {
return this.scheduledExecutor.schedule(decorateTask(task, false), initialDelay, TimeUnit.MILLISECONDS); return this.scheduledExecutor.schedule(decorateTask(task, false), initialDelay.toMillis(), TimeUnit.MILLISECONDS);
} }
catch (RejectedExecutionException ex) { catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex); throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
@ -217,10 +220,10 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
} }
@Override @Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) { public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) {
long initialDelay = startTime.getTime() - this.clock.millis(); Duration initialDelay = Duration.between(this.clock.instant(), startTime);
try { try {
return this.scheduledExecutor.scheduleAtFixedRate(decorateTask(task, true), initialDelay, period, TimeUnit.MILLISECONDS); return this.scheduledExecutor.scheduleAtFixedRate(decorateTask(task, true), initialDelay.toMillis(), period.toMillis(), TimeUnit.MILLISECONDS);
} }
catch (RejectedExecutionException ex) { catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex); throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
@ -228,9 +231,9 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
} }
@Override @Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) { public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period) {
try { try {
return this.scheduledExecutor.scheduleAtFixedRate(decorateTask(task, true), 0, period, TimeUnit.MILLISECONDS); return this.scheduledExecutor.scheduleAtFixedRate(decorateTask(task, true), 0, period.toMillis(), TimeUnit.MILLISECONDS);
} }
catch (RejectedExecutionException ex) { catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex); throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
@ -238,10 +241,10 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
} }
@Override @Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay) { public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) {
long initialDelay = startTime.getTime() - this.clock.millis(); Duration initialDelay = Duration.between(this.clock.instant(), startTime);
try { try {
return this.scheduledExecutor.scheduleWithFixedDelay(decorateTask(task, true), initialDelay, delay, TimeUnit.MILLISECONDS); return this.scheduledExecutor.scheduleWithFixedDelay(decorateTask(task, true), initialDelay.toMillis(), delay.toMillis(), TimeUnit.MILLISECONDS);
} }
catch (RejectedExecutionException ex) { catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex); throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
@ -249,9 +252,9 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
} }
@Override @Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay) { public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay) {
try { try {
return this.scheduledExecutor.scheduleWithFixedDelay(decorateTask(task, true), 0, delay, TimeUnit.MILLISECONDS); return this.scheduledExecutor.scheduleWithFixedDelay(decorateTask(task, true), 0, delay.toMillis(), TimeUnit.MILLISECONDS);
} }
catch (RejectedExecutionException ex) { catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex); throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
@ -273,22 +276,66 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
*/ */
private class EnterpriseConcurrentTriggerScheduler { private class EnterpriseConcurrentTriggerScheduler {
public ScheduledFuture<?> schedule(Runnable task, final Trigger trigger) { public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
ManagedScheduledExecutorService executor = (ManagedScheduledExecutorService) scheduledExecutor; ManagedScheduledExecutorService executor = (ManagedScheduledExecutorService) scheduledExecutor;
return executor.schedule(task, new jakarta.enterprise.concurrent.Trigger() { return executor.schedule(task, new TriggerAdapter(trigger));
@Override
@Nullable
public Date getNextRunTime(@Nullable LastExecution le, Date taskScheduledTime) {
return (trigger.nextExecutionTime(le != null ?
new SimpleTriggerContext(le.getScheduledStart(), le.getRunStart(), le.getRunEnd()) :
new SimpleTriggerContext()));
}
@Override
public boolean skipRun(LastExecution lastExecution, Date scheduledRunTime) {
return false;
}
});
} }
private static class TriggerAdapter implements jakarta.enterprise.concurrent.Trigger {
private final Trigger adaptee;
public TriggerAdapter(Trigger adaptee) {
this.adaptee = adaptee;
}
@Override
@Nullable
public Date getNextRunTime(@Nullable LastExecution le, Date taskScheduledTime) {
Instant instant = this.adaptee.nextExecution(new LastExecutionAdapter(le));
return instant != null ? Date.from(instant) : null;
}
@Nullable
private static Instant toInstant(@Nullable Date date) {
return date != null ? date.toInstant() : null;
}
@Override
public boolean skipRun(LastExecution lastExecutionInfo, Date scheduledRunTime) {
return false;
}
private static class LastExecutionAdapter implements TriggerContext {
@Nullable
private final LastExecution le;
public LastExecutionAdapter(@Nullable LastExecution le) {
this.le = le;
}
@Override
public Instant lastScheduledExecution() {
return (this.le != null) ? toInstant(this.le.getScheduledStart()) : null;
}
@Override
public Instant lastActualExecution() {
return (this.le != null) ? toInstant(this.le.getRunStart()) : null;
}
@Override
public Instant lastCompletion() {
return (this.le != null) ? toInstant(this.le.getRunEnd()) : null;
}
}
}
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,7 +17,8 @@
package org.springframework.scheduling.concurrent; package org.springframework.scheduling.concurrent;
import java.time.Clock; import java.time.Clock;
import java.util.Date; import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.Delayed; import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@ -56,7 +57,7 @@ class ReschedulingRunnable extends DelegatingErrorHandlingRunnable implements Sc
private ScheduledFuture<?> currentFuture; private ScheduledFuture<?> currentFuture;
@Nullable @Nullable
private Date scheduledExecutionTime; private Instant scheduledExecutionTime;
private final Object triggerContextMonitor = new Object(); private final Object triggerContextMonitor = new Object();
@ -74,12 +75,12 @@ class ReschedulingRunnable extends DelegatingErrorHandlingRunnable implements Sc
@Nullable @Nullable
public ScheduledFuture<?> schedule() { public ScheduledFuture<?> schedule() {
synchronized (this.triggerContextMonitor) { synchronized (this.triggerContextMonitor) {
this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext); this.scheduledExecutionTime = this.trigger.nextExecution(this.triggerContext);
if (this.scheduledExecutionTime == null) { if (this.scheduledExecutionTime == null) {
return null; return null;
} }
long initialDelay = this.scheduledExecutionTime.getTime() - this.triggerContext.getClock().millis(); Duration initialDelay = Duration.between(this.triggerContext.getClock().instant(), this.scheduledExecutionTime);
this.currentFuture = this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS); this.currentFuture = this.executor.schedule(this, initialDelay.toMillis(), TimeUnit.MILLISECONDS);
return this; return this;
} }
} }
@ -91,9 +92,9 @@ class ReschedulingRunnable extends DelegatingErrorHandlingRunnable implements Sc
@Override @Override
public void run() { public void run() {
Date actualExecutionTime = new Date(this.triggerContext.getClock().millis()); Instant actualExecutionTime = this.triggerContext.getClock().instant();
super.run(); super.run();
Date completionTime = new Date(this.triggerContext.getClock().millis()); Instant completionTime = this.triggerContext.getClock().instant();
synchronized (this.triggerContextMonitor) { synchronized (this.triggerContextMonitor) {
Assert.state(this.scheduledExecutionTime != null, "No scheduled execution"); Assert.state(this.scheduledExecutionTime != null, "No scheduled execution");
this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime); this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime);

View File

@ -17,7 +17,8 @@
package org.springframework.scheduling.concurrent; package org.springframework.scheduling.concurrent;
import java.time.Clock; import java.time.Clock;
import java.util.Date; import java.time.Duration;
import java.time.Instant;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@ -377,11 +378,11 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
} }
@Override @Override
public ScheduledFuture<?> schedule(Runnable task, Date startTime) { public ScheduledFuture<?> schedule(Runnable task, Instant startTime) {
ScheduledExecutorService executor = getScheduledExecutor(); ScheduledExecutorService executor = getScheduledExecutor();
long initialDelay = startTime.getTime() - this.clock.millis(); Duration initialDelay = Duration.between(this.clock.instant(), startTime);
try { try {
return executor.schedule(errorHandlingTask(task, false), initialDelay, TimeUnit.MILLISECONDS); return executor.schedule(errorHandlingTask(task, false), initialDelay.toMillis(), TimeUnit.MILLISECONDS);
} }
catch (RejectedExecutionException ex) { catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
@ -389,11 +390,11 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
} }
@Override @Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) { public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) {
ScheduledExecutorService executor = getScheduledExecutor(); ScheduledExecutorService executor = getScheduledExecutor();
long initialDelay = startTime.getTime() - this.clock.millis(); Duration initialDelay = Duration.between(this.clock.instant(), startTime);
try { try {
return executor.scheduleAtFixedRate(errorHandlingTask(task, true), initialDelay, period, TimeUnit.MILLISECONDS); return executor.scheduleAtFixedRate(errorHandlingTask(task, true), initialDelay.toMillis(), period.toMillis(), TimeUnit.MILLISECONDS);
} }
catch (RejectedExecutionException ex) { catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
@ -401,10 +402,10 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
} }
@Override @Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) { public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period) {
ScheduledExecutorService executor = getScheduledExecutor(); ScheduledExecutorService executor = getScheduledExecutor();
try { try {
return executor.scheduleAtFixedRate(errorHandlingTask(task, true), 0, period, TimeUnit.MILLISECONDS); return executor.scheduleAtFixedRate(errorHandlingTask(task, true), 0, period.toMillis(), TimeUnit.MILLISECONDS);
} }
catch (RejectedExecutionException ex) { catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
@ -412,11 +413,11 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
} }
@Override @Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay) { public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) {
ScheduledExecutorService executor = getScheduledExecutor(); ScheduledExecutorService executor = getScheduledExecutor();
long initialDelay = startTime.getTime() - this.clock.millis(); Duration initialDelay = Duration.between(this.clock.instant(), startTime);
try { try {
return executor.scheduleWithFixedDelay(errorHandlingTask(task, true), initialDelay, delay, TimeUnit.MILLISECONDS); return executor.scheduleWithFixedDelay(errorHandlingTask(task, true), initialDelay.toMillis(), delay.toMillis(), TimeUnit.MILLISECONDS);
} }
catch (RejectedExecutionException ex) { catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
@ -424,10 +425,10 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
} }
@Override @Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay) { public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay) {
ScheduledExecutorService executor = getScheduledExecutor(); ScheduledExecutorService executor = getScheduledExecutor();
try { try {
return executor.scheduleWithFixedDelay(errorHandlingTask(task, true), 0, delay, TimeUnit.MILLISECONDS); return executor.scheduleWithFixedDelay(errorHandlingTask(task, true), 0, delay.toMillis(), TimeUnit.MILLISECONDS);
} }
catch (RejectedExecutionException ex) { catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,10 +16,13 @@
package org.springframework.scheduling.config; package org.springframework.scheduling.config;
import java.time.Duration;
/** /**
* Specialization of {@link IntervalTask} for fixed-delay semantics. * Specialization of {@link IntervalTask} for fixed-delay semantics.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Arjen Poutsma
* @since 5.0.2 * @since 5.0.2
* @see org.springframework.scheduling.annotation.Scheduled#fixedDelay() * @see org.springframework.scheduling.annotation.Scheduled#fixedDelay()
* @see ScheduledTaskRegistrar#addFixedDelayTask(IntervalTask) * @see ScheduledTaskRegistrar#addFixedDelayTask(IntervalTask)
@ -31,9 +34,26 @@ public class FixedDelayTask extends IntervalTask {
* @param runnable the underlying task to execute * @param runnable the underlying task to execute
* @param interval how often in milliseconds the task should be executed * @param interval how often in milliseconds the task should be executed
* @param initialDelay the initial delay before first execution of the task * @param initialDelay the initial delay before first execution of the task
* @deprecated as of 6.0, in favor on {@link #FixedDelayTask(Runnable, Duration, Duration)}
*/ */
@Deprecated
public FixedDelayTask(Runnable runnable, long interval, long initialDelay) { public FixedDelayTask(Runnable runnable, long interval, long initialDelay) {
super(runnable, interval, initialDelay); super(runnable, interval, initialDelay);
} }
/**
* Create a new {@code FixedDelayTask}.
* @param runnable the underlying task to execute
* @param interval how often the task should be executed
* @param initialDelay the initial delay before first execution of the task
* @since 6.0
*/
public FixedDelayTask(Runnable runnable, Duration interval, Duration initialDelay) {
super(runnable, interval, initialDelay);
}
FixedDelayTask(IntervalTask task) {
super(task);
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,10 +16,13 @@
package org.springframework.scheduling.config; package org.springframework.scheduling.config;
import java.time.Duration;
/** /**
* Specialization of {@link IntervalTask} for fixed-rate semantics. * Specialization of {@link IntervalTask} for fixed-rate semantics.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Arjen Poutsma
* @since 5.0.2 * @since 5.0.2
* @see org.springframework.scheduling.annotation.Scheduled#fixedRate() * @see org.springframework.scheduling.annotation.Scheduled#fixedRate()
* @see ScheduledTaskRegistrar#addFixedRateTask(IntervalTask) * @see ScheduledTaskRegistrar#addFixedRateTask(IntervalTask)
@ -31,9 +34,27 @@ public class FixedRateTask extends IntervalTask {
* @param runnable the underlying task to execute * @param runnable the underlying task to execute
* @param interval how often in milliseconds the task should be executed * @param interval how often in milliseconds the task should be executed
* @param initialDelay the initial delay before first execution of the task * @param initialDelay the initial delay before first execution of the task
* @deprecated as of 6.0, in favor on {@link #FixedRateTask(Runnable, Duration, Duration)}
*/ */
@Deprecated
public FixedRateTask(Runnable runnable, long interval, long initialDelay) { public FixedRateTask(Runnable runnable, long interval, long initialDelay) {
super(runnable, interval, initialDelay); super(runnable, interval, initialDelay);
} }
/**
* Create a new {@code FixedRateTask}.
* @param runnable the underlying task to execute
* @param interval how often the task should be executed
* @param initialDelay the initial delay before first execution of the task
* @since 6.0
*/
public FixedRateTask(Runnable runnable, Duration interval, Duration initialDelay) {
super(runnable, interval, initialDelay);
}
FixedRateTask(IntervalTask task) {
super(task);
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,21 +16,26 @@
package org.springframework.scheduling.config; package org.springframework.scheduling.config;
import java.time.Duration;
import org.springframework.util.Assert;
/** /**
* {@link Task} implementation defining a {@code Runnable} to be executed at a given * {@link Task} implementation defining a {@code Runnable} to be executed at a given
* millisecond interval which may be treated as fixed-rate or fixed-delay depending on * millisecond interval which may be treated as fixed-rate or fixed-delay depending on
* context. * context.
* *
* @author Chris Beams * @author Chris Beams
* @author Arjen Poutsma
* @since 3.2 * @since 3.2
* @see ScheduledTaskRegistrar#addFixedRateTask(IntervalTask) * @see ScheduledTaskRegistrar#addFixedRateTask(IntervalTask)
* @see ScheduledTaskRegistrar#addFixedDelayTask(IntervalTask) * @see ScheduledTaskRegistrar#addFixedDelayTask(IntervalTask)
*/ */
public class IntervalTask extends Task { public class IntervalTask extends Task {
private final long interval; private final Duration interval;
private final long initialDelay; private final Duration initialDelay;
/** /**
@ -38,34 +43,95 @@ public class IntervalTask extends Task {
* @param runnable the underlying task to execute * @param runnable the underlying task to execute
* @param interval how often in milliseconds the task should be executed * @param interval how often in milliseconds the task should be executed
* @param initialDelay the initial delay before first execution of the task * @param initialDelay the initial delay before first execution of the task
* @deprecated as of 6.0, in favor on {@link #IntervalTask(Runnable, Duration, Duration)}
*/ */
@Deprecated
public IntervalTask(Runnable runnable, long interval, long initialDelay) { public IntervalTask(Runnable runnable, long interval, long initialDelay) {
super(runnable); this(runnable, Duration.ofMillis(interval), Duration.ofMillis(initialDelay));
this.interval = interval;
this.initialDelay = initialDelay;
} }
/** /**
* Create a new {@code IntervalTask} with no initial delay. * Create a new {@code IntervalTask} with no initial delay.
* @param runnable the underlying task to execute * @param runnable the underlying task to execute
* @param interval how often in milliseconds the task should be executed * @param interval how often in milliseconds the task should be executed
* @deprecated as of 6.0, in favor on {@link #IntervalTask(Runnable, Duration)}
*/ */
@Deprecated
public IntervalTask(Runnable runnable, long interval) { public IntervalTask(Runnable runnable, long interval) {
this(runnable, interval, 0); this(runnable, Duration.ofMillis(interval), Duration.ZERO);
} }
/**
* Create a new {@code IntervalTask} with no initial delay.
* @param runnable the underlying task to execute
* @param interval how often the task should be executed
* @since 6.0
*/
public IntervalTask(Runnable runnable, Duration interval) {
this(runnable, interval, Duration.ZERO);
}
/**
* Create a new {@code IntervalTask}.
* @param runnable the underlying task to execute
* @param interval how often the task should be executed
* @param initialDelay the initial delay before first execution of the task
* @since 6.0
*/
public IntervalTask(Runnable runnable, Duration interval, Duration initialDelay) {
super(runnable);
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");
this.interval = task.getIntervalDuration();
this.initialDelay = task.getInitialDelayDuration();
}
/** /**
* Return how often in milliseconds the task should be executed. * Return how often in milliseconds the task should be executed.
* @deprecated as of 6.0, in favor of {@link #getIntervalDuration()}
*/ */
@Deprecated
public long getInterval() { public long getInterval() {
return this.interval.toMillis();
}
/**
* Return how often the task should be executed.
* @since 6.0
*/
public Duration getIntervalDuration() {
return this.interval; return this.interval;
} }
/** /**
* Return the initial delay before first execution of the task. * Return the initial delay before first execution of the task.
* @deprecated as of 6.0, in favor of {@link #getInitialDelayDuration()}
*/ */
@Deprecated
public long getInitialDelay() { public long getInitialDelay() {
return this.initialDelay.toMillis();
}
/**
* Return the initial delay before first execution of the task.
* @since 6.0
*/
public Duration getInitialDelayDuration() {
return this.initialDelay; return this.initialDelay;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,9 +16,10 @@
package org.springframework.scheduling.config; package org.springframework.scheduling.config;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
@ -51,6 +52,7 @@ import org.springframework.util.CollectionUtils;
* @author Chris Beams * @author Chris Beams
* @author Tobias Montagna-Hay * @author Tobias Montagna-Hay
* @author Sam Brannen * @author Sam Brannen
* @author Arjen Poutsma
* @since 3.0 * @since 3.0
* @see org.springframework.scheduling.annotation.EnableAsync * @see org.springframework.scheduling.annotation.EnableAsync
* @see org.springframework.scheduling.annotation.SchedulingConfigurer * @see org.springframework.scheduling.annotation.SchedulingConfigurer
@ -189,11 +191,11 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
/** /**
* Specify triggered tasks as a Map of Runnables (the tasks) and fixed-rate values. * Specify triggered tasks as a Map of Runnables (the tasks) and fixed-rate values.
* @see TaskScheduler#scheduleAtFixedRate(Runnable, long) * @see TaskScheduler#scheduleAtFixedRate(Runnable, Duration)
*/ */
public void setFixedRateTasks(Map<Runnable, Long> fixedRateTasks) { public void setFixedRateTasks(Map<Runnable, Long> fixedRateTasks) {
this.fixedRateTasks = new ArrayList<>(); this.fixedRateTasks = new ArrayList<>();
fixedRateTasks.forEach(this::addFixedRateTask); fixedRateTasks.forEach((task, interval) -> addFixedRateTask(task, Duration.ofMillis(interval)));
} }
/** /**
@ -218,11 +220,11 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
/** /**
* Specify triggered tasks as a Map of Runnables (the tasks) and fixed-delay values. * Specify triggered tasks as a Map of Runnables (the tasks) and fixed-delay values.
* @see TaskScheduler#scheduleWithFixedDelay(Runnable, long) * @see TaskScheduler#scheduleWithFixedDelay(Runnable, Duration)
*/ */
public void setFixedDelayTasks(Map<Runnable, Long> fixedDelayTasks) { public void setFixedDelayTasks(Map<Runnable, Long> fixedDelayTasks) {
this.fixedDelayTasks = new ArrayList<>(); this.fixedDelayTasks = new ArrayList<>();
fixedDelayTasks.forEach(this::addFixedDelayTask); fixedDelayTasks.forEach((task, delay) -> addFixedDelayTask(task, Duration.ofMillis(delay)));
} }
/** /**
@ -248,7 +250,7 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
/** /**
* Add a Runnable task to be triggered per the given {@link Trigger}. * Add a Runnable task to be triggered per the given {@link Trigger}.
* @see TaskScheduler#scheduleAtFixedRate(Runnable, long) * @see TaskScheduler#scheduleAtFixedRate(Runnable, Duration)
*/ */
public void addTriggerTask(Runnable task, Trigger trigger) { public void addTriggerTask(Runnable task, Trigger trigger) {
addTriggerTask(new TriggerTask(task, trigger)); addTriggerTask(new TriggerTask(task, trigger));
@ -257,7 +259,7 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
/** /**
* Add a {@code TriggerTask}. * Add a {@code TriggerTask}.
* @since 3.2 * @since 3.2
* @see TaskScheduler#scheduleAtFixedRate(Runnable, long) * @see TaskScheduler#scheduleAtFixedRate(Runnable, Duration)
*/ */
public void addTriggerTask(TriggerTask task) { public void addTriggerTask(TriggerTask task) {
if (this.triggerTasks == null) { if (this.triggerTasks == null) {
@ -290,16 +292,26 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
/** /**
* Add a {@code Runnable} task to be triggered at the given fixed-rate interval. * Add a {@code Runnable} task to be triggered at the given fixed-rate interval.
* @see TaskScheduler#scheduleAtFixedRate(Runnable, long) * @deprecated as of 6.0, in favor of {@link #addFixedRateTask(Runnable, Duration)}
*/ */
@Deprecated
public void addFixedRateTask(Runnable task, long interval) { public void addFixedRateTask(Runnable task, long interval) {
addFixedRateTask(new IntervalTask(task, interval, 0)); addFixedRateTask(new IntervalTask(task, Duration.ofMillis(interval)));
}
/**
* Add a {@code Runnable} task to be triggered at the given fixed-rate interval.
* @since 6.0
* @see TaskScheduler#scheduleAtFixedRate(Runnable, Duration)
*/
public void addFixedRateTask(Runnable task, Duration interval) {
addFixedRateTask(new IntervalTask(task, interval));
} }
/** /**
* Add a fixed-rate {@link IntervalTask}. * Add a fixed-rate {@link IntervalTask}.
* @since 3.2 * @since 3.2
* @see TaskScheduler#scheduleAtFixedRate(Runnable, long) * @see TaskScheduler#scheduleAtFixedRate(Runnable, Duration)
*/ */
public void addFixedRateTask(IntervalTask task) { public void addFixedRateTask(IntervalTask task) {
if (this.fixedRateTasks == null) { if (this.fixedRateTasks == null) {
@ -310,16 +322,26 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
/** /**
* Add a Runnable task to be triggered with the given fixed delay. * Add a Runnable task to be triggered with the given fixed delay.
* @see TaskScheduler#scheduleWithFixedDelay(Runnable, long) * @deprecated as of 6.0, in favor of {@link #addFixedDelayTask(Runnable, Duration)}
*/ */
@Deprecated
public void addFixedDelayTask(Runnable task, long delay) { public void addFixedDelayTask(Runnable task, long delay) {
addFixedDelayTask(new IntervalTask(task, delay, 0)); addFixedDelayTask(new IntervalTask(task, Duration.ofMillis(delay)));
}
/**
* Add a Runnable task to be triggered with the given fixed delay.
* @since 6.0
* @see TaskScheduler#scheduleWithFixedDelay(Runnable, Duration)
*/
public void addFixedDelayTask(Runnable task, Duration delay) {
addFixedDelayTask(new IntervalTask(task, delay));
} }
/** /**
* Add a fixed-delay {@link IntervalTask}. * Add a fixed-delay {@link IntervalTask}.
* @since 3.2 * @since 3.2
* @see TaskScheduler#scheduleWithFixedDelay(Runnable, long) * @see TaskScheduler#scheduleWithFixedDelay(Runnable, Duration)
*/ */
public void addFixedDelayTask(IntervalTask task) { public void addFixedDelayTask(IntervalTask task) {
if (this.fixedDelayTasks == null) { if (this.fixedDelayTasks == null) {
@ -353,7 +375,6 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
* Schedule all registered tasks against the underlying * Schedule all registered tasks against the underlying
* {@linkplain #setTaskScheduler(TaskScheduler) task scheduler}. * {@linkplain #setTaskScheduler(TaskScheduler) task scheduler}.
*/ */
@SuppressWarnings("deprecation")
protected void scheduleTasks() { protected void scheduleTasks() {
if (this.taskScheduler == null) { if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor(); this.localExecutor = Executors.newSingleThreadScheduledExecutor();
@ -371,12 +392,22 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
} }
if (this.fixedRateTasks != null) { if (this.fixedRateTasks != null) {
for (IntervalTask task : this.fixedRateTasks) { for (IntervalTask task : this.fixedRateTasks) {
addScheduledTask(scheduleFixedRateTask(task)); if (task instanceof FixedRateTask fixedRateTask) {
addScheduledTask(scheduleFixedRateTask(fixedRateTask));
}
else {
addScheduledTask(scheduleFixedRateTask(new FixedRateTask(task)));
}
} }
} }
if (this.fixedDelayTasks != null) { if (this.fixedDelayTasks != null) {
for (IntervalTask task : this.fixedDelayTasks) { for (IntervalTask task : this.fixedDelayTasks) {
addScheduledTask(scheduleFixedDelayTask(task)); if (task instanceof FixedDelayTask fixedDelayTask) {
addScheduledTask(scheduleFixedDelayTask(fixedDelayTask));
}
else {
addScheduledTask(scheduleFixedDelayTask(new FixedDelayTask(task)));
}
} }
} }
} }
@ -437,22 +468,6 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
return (newTask ? scheduledTask : null); return (newTask ? scheduledTask : null);
} }
/**
* Schedule the specified fixed-rate 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 4.3
* @deprecated as of 5.0.2, in favor of {@link #scheduleFixedRateTask(FixedRateTask)}
*/
@Deprecated
@Nullable
public ScheduledTask scheduleFixedRateTask(IntervalTask task) {
FixedRateTask taskToUse = (task instanceof FixedRateTask ? (FixedRateTask) task :
new FixedRateTask(task.getRunnable(), task.getInterval(), task.getInitialDelay()));
return scheduleFixedRateTask(taskToUse);
}
/** /**
* Schedule the specified fixed-rate task, either right away if possible * Schedule the specified fixed-rate task, either right away if possible
* or on initialization of the scheduler. * or on initialization of the scheduler.
@ -469,14 +484,15 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
newTask = true; newTask = true;
} }
if (this.taskScheduler != null) { if (this.taskScheduler != null) {
if (task.getInitialDelay() > 0) { Duration initialDelay = task.getInitialDelayDuration();
Date startTime = new Date(this.taskScheduler.getClock().millis() + task.getInitialDelay()); if (initialDelay.toMillis() > 0) {
Instant startTime = this.taskScheduler.getClock().instant().plus(initialDelay);
scheduledTask.future = scheduledTask.future =
this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), startTime, task.getInterval()); this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), startTime, task.getIntervalDuration());
} }
else { else {
scheduledTask.future = scheduledTask.future =
this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), task.getInterval()); this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), task.getIntervalDuration());
} }
} }
else { else {
@ -486,22 +502,6 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
return (newTask ? scheduledTask : null); return (newTask ? scheduledTask : null);
} }
/**
* Schedule the specified fixed-delay 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 4.3
* @deprecated as of 5.0.2, in favor of {@link #scheduleFixedDelayTask(FixedDelayTask)}
*/
@Deprecated
@Nullable
public ScheduledTask scheduleFixedDelayTask(IntervalTask task) {
FixedDelayTask taskToUse = (task instanceof FixedDelayTask ? (FixedDelayTask) task :
new FixedDelayTask(task.getRunnable(), task.getInterval(), task.getInitialDelay()));
return scheduleFixedDelayTask(taskToUse);
}
/** /**
* Schedule the specified fixed-delay task, either right away if possible * Schedule the specified fixed-delay task, either right away if possible
* or on initialization of the scheduler. * or on initialization of the scheduler.
@ -518,14 +518,15 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
newTask = true; newTask = true;
} }
if (this.taskScheduler != null) { if (this.taskScheduler != null) {
if (task.getInitialDelay() > 0) { Duration initialDelay = task.getInitialDelayDuration();
Date startTime = new Date(this.taskScheduler.getClock().millis() + task.getInitialDelay()); if (!initialDelay.isNegative()) {
Instant startTime = this.taskScheduler.getClock().instant().plus(task.getInitialDelayDuration());
scheduledTask.future = scheduledTask.future =
this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), startTime, task.getInterval()); this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), startTime, task.getIntervalDuration());
} }
else { else {
scheduledTask.future = scheduledTask.future =
this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), task.getInterval()); this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), task.getIntervalDuration());
} }
} }
else { else {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,9 +16,9 @@
package org.springframework.scheduling.support; package org.springframework.scheduling.support;
import java.time.Instant;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Date;
import java.util.TimeZone; import java.util.TimeZone;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -93,23 +93,23 @@ public class CronTrigger implements Trigger {
* previous execution; therefore, overlapping executions won't occur. * previous execution; therefore, overlapping executions won't occur.
*/ */
@Override @Override
public Date nextExecutionTime(TriggerContext triggerContext) { public Instant nextExecution(TriggerContext triggerContext) {
Date date = triggerContext.lastCompletionTime(); Instant instant = triggerContext.lastCompletion();
if (date != null) { if (instant != null) {
Date scheduled = triggerContext.lastScheduledExecutionTime(); Instant scheduled = triggerContext.lastScheduledExecution();
if (scheduled != null && date.before(scheduled)) { if (scheduled != null && instant.isBefore(scheduled)) {
// Previous task apparently executed too early... // Previous task apparently executed too early...
// Let's simply use the last calculated execution time then, // Let's simply use the last calculated execution time then,
// in order to prevent accidental re-fires in the same second. // in order to prevent accidental re-fires in the same second.
date = scheduled; instant = scheduled;
} }
} }
else { else {
date = new Date(triggerContext.getClock().millis()); instant = triggerContext.getClock().instant();
} }
ZonedDateTime dateTime = ZonedDateTime.ofInstant(date.toInstant(), this.zoneId); ZonedDateTime dateTime = ZonedDateTime.ofInstant(instant, this.zoneId);
ZonedDateTime next = this.expression.next(dateTime); ZonedDateTime next = this.expression.next(dateTime);
return (next != null ? Date.from(next.toInstant()) : null); return (next != null ? next.toInstant() : null);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,10 @@
package org.springframework.scheduling.support; package org.springframework.scheduling.support;
import java.util.Date; import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -46,18 +49,22 @@ import org.springframework.util.Assert;
*/ */
public class PeriodicTrigger implements Trigger { public class PeriodicTrigger implements Trigger {
private final long period; private final Duration period;
private final TimeUnit timeUnit; @Nullable
private final ChronoUnit chronoUnit;
private volatile long initialDelay; @Nullable
private volatile Duration initialDelay;
private volatile boolean fixedRate; private volatile boolean fixedRate;
/** /**
* Create a trigger with the given period in milliseconds. * Create a trigger with the given period in milliseconds.
* @deprecated as of 6.0, in favor on {@link #PeriodicTrigger(Duration)}
*/ */
@Deprecated
public PeriodicTrigger(long period) { public PeriodicTrigger(long period) {
this(period, null); this(period, null);
} }
@ -66,44 +73,132 @@ public class PeriodicTrigger implements Trigger {
* Create a trigger with the given period and time unit. The time unit will * Create a trigger with the given period and time unit. The time unit will
* apply not only to the period but also to any 'initialDelay' value, if * apply not only to the period but also to any 'initialDelay' value, if
* configured on this Trigger later via {@link #setInitialDelay(long)}. * configured on this Trigger later via {@link #setInitialDelay(long)}.
* @deprecated as of 6.0, in favor on {@link #PeriodicTrigger(Duration)}
*/ */
@Deprecated
public PeriodicTrigger(long period, @Nullable TimeUnit timeUnit) { public PeriodicTrigger(long period, @Nullable TimeUnit timeUnit) {
Assert.isTrue(period >= 0, "period must not be negative"); this(toDuration(period, timeUnit), timeUnit);
this.timeUnit = (timeUnit != null ? timeUnit : TimeUnit.MILLISECONDS); }
this.period = this.timeUnit.toMillis(period);
private static Duration toDuration(long amount, @Nullable TimeUnit timeUnit) {
if (timeUnit != null) {
return Duration.of(amount, timeUnit.toChronoUnit());
}
else {
return Duration.ofMillis(amount);
}
}
/**
* Create a trigger with the given period as a duration.
* @since 6.0
*/
public PeriodicTrigger(Duration period) {
this(period, null);
}
private PeriodicTrigger(Duration period, @Nullable TimeUnit timeUnit) {
Assert.notNull(period, "Period must not be null");
Assert.isTrue(!period.isNegative(), "Period must not be negative");
this.period = period;
if (timeUnit != null) {
this.chronoUnit = timeUnit.toChronoUnit();
}
else {
this.chronoUnit = null;
}
} }
/** /**
* Return this trigger's period. * Return this trigger's period.
* @since 5.0.2 * @since 5.0.2
* @deprecated as of 6.0, in favor on {@link #getPeriodDuration()}
*/ */
@Deprecated
public long getPeriod() { public long getPeriod() {
if (this.chronoUnit != null) {
return this.period.get(this.chronoUnit);
}
else {
return this.period.toMillis();
}
}
/**
* Return this trigger's period.
* @since 6.0
*/
public Duration getPeriodDuration() {
return this.period; return this.period;
} }
/** /**
* Return this trigger's time unit (milliseconds by default). * Return this trigger's time unit (milliseconds by default).
* @since 5.0.2 * @since 5.0.2
* @deprecated as of 6.0, with no direct replacement
*/ */
@Deprecated
public TimeUnit getTimeUnit() { public TimeUnit getTimeUnit() {
return this.timeUnit; if (this.chronoUnit != null) {
return TimeUnit.of(this.chronoUnit);
}
else {
return TimeUnit.MILLISECONDS;
}
} }
/** /**
* Specify the delay for the initial execution. It will be evaluated in * Specify the delay for the initial execution. It will be evaluated in
* terms of this trigger's {@link TimeUnit}. If no time unit was explicitly * terms of this trigger's {@link TimeUnit}. If no time unit was explicitly
* provided upon instantiation, the default is milliseconds. * provided upon instantiation, the default is milliseconds.
* @deprecated as of 6.0, in favor of {@link #setInitialDelay(Duration)}
*/ */
@Deprecated
public void setInitialDelay(long initialDelay) { public void setInitialDelay(long initialDelay) {
this.initialDelay = this.timeUnit.toMillis(initialDelay); if (this.chronoUnit != null) {
this.initialDelay = Duration.of(initialDelay, this.chronoUnit);
}
else {
this.initialDelay = Duration.ofMillis(initialDelay);
}
}
/**
* Specify the delay for the initial execution.
* @since 6.0
*/
public void setInitialDelay(Duration initialDelay) {
this.initialDelay = initialDelay;
} }
/** /**
* Return the initial delay, or 0 if none. * Return the initial delay, or 0 if none.
* @since 5.0.2 * @since 5.0.2
* @deprecated as of 6.0, in favor on {@link #getInitialDelayDuration()}
*/ */
@Deprecated
public long getInitialDelay() { public long getInitialDelay() {
Duration initialDelay = this.initialDelay;
if (initialDelay != null) {
if (this.chronoUnit != null) {
return initialDelay.get(this.chronoUnit);
}
else {
return initialDelay.toMillis();
}
}
else {
return 0;
}
}
/**
* Return the initial delay, or {@code null} if none.
* @since 6.0
*/
@Nullable
public Duration getInitialDelayDuration() {
return this.initialDelay; return this.initialDelay;
} }
@ -130,16 +225,23 @@ public class PeriodicTrigger implements Trigger {
* Returns the time after which a task should run again. * Returns the time after which a task should run again.
*/ */
@Override @Override
public Date nextExecutionTime(TriggerContext triggerContext) { public Instant nextExecution(TriggerContext triggerContext) {
Date lastExecution = triggerContext.lastScheduledExecutionTime(); Instant lastExecution = triggerContext.lastScheduledExecution();
Date lastCompletion = triggerContext.lastCompletionTime(); Instant lastCompletion = triggerContext.lastCompletion();
if (lastExecution == null || lastCompletion == null) { if (lastExecution == null || lastCompletion == null) {
return new Date(triggerContext.getClock().millis() + this.initialDelay); Instant instant = triggerContext.getClock().instant();
Duration initialDelay = this.initialDelay;
if (initialDelay == null) {
return instant;
}
else {
return instant.plus(initialDelay);
}
} }
if (this.fixedRate) { if (this.fixedRate) {
return new Date(lastExecution.getTime() + this.period); return lastExecution.plus(this.period);
} }
return new Date(lastCompletion.getTime() + this.period); return lastCompletion.plus(this.period);
} }
@ -151,13 +253,14 @@ public class PeriodicTrigger implements Trigger {
if (!(other instanceof PeriodicTrigger otherTrigger)) { if (!(other instanceof PeriodicTrigger otherTrigger)) {
return false; return false;
} }
return (this.fixedRate == otherTrigger.fixedRate && this.initialDelay == otherTrigger.initialDelay && return (this.fixedRate == otherTrigger.fixedRate &&
this.period == otherTrigger.period); this.period.equals(otherTrigger.period) &&
Objects.equals(this.initialDelay, otherTrigger.initialDelay));
} }
@Override @Override
public int hashCode() { public int hashCode() {
return (this.fixedRate ? 17 : 29) + (int) (37 * this.period) + (int) (41 * this.initialDelay); return this.period.hashCode();
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package org.springframework.scheduling.support; package org.springframework.scheduling.support;
import java.time.Clock; import java.time.Clock;
import java.time.Instant;
import java.util.Date; import java.util.Date;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -33,13 +34,13 @@ public class SimpleTriggerContext implements TriggerContext {
private final Clock clock; private final Clock clock;
@Nullable @Nullable
private volatile Date lastScheduledExecutionTime; private volatile Instant lastScheduledExecution;
@Nullable @Nullable
private volatile Date lastActualExecutionTime; private volatile Instant lastActualExecution;
@Nullable @Nullable
private volatile Date lastCompletionTime; private volatile Instant lastCompletion;
/** /**
@ -56,12 +57,34 @@ public class SimpleTriggerContext implements TriggerContext {
* @param lastScheduledExecutionTime last <i>scheduled</i> execution time * @param lastScheduledExecutionTime last <i>scheduled</i> execution time
* @param lastActualExecutionTime last <i>actual</i> execution time * @param lastActualExecutionTime last <i>actual</i> execution time
* @param lastCompletionTime last completion time * @param lastCompletionTime last completion time
* @deprecated as of 6.0, in favor of {@link #SimpleTriggerContext(Instant, Instant, Instant)}
*/ */
public SimpleTriggerContext(Date lastScheduledExecutionTime, Date lastActualExecutionTime, Date lastCompletionTime) { @Deprecated
public SimpleTriggerContext(@Nullable Date lastScheduledExecutionTime, @Nullable Date lastActualExecutionTime,
@Nullable Date lastCompletionTime) {
this(toInstant(lastScheduledExecutionTime), toInstant(lastActualExecutionTime), toInstant(lastCompletionTime));
}
@Nullable
private static Instant toInstant(@Nullable Date date) {
return date != null ? date.toInstant() : null;
}
/**
* Create a SimpleTriggerContext with the given time values,
* exposing the system clock for the default time zone.
* @param lastScheduledExecution last <i>scheduled</i> execution time
* @param lastActualExecution last <i>actual</i> execution time
* @param lastCompletion last completion time
*/
public SimpleTriggerContext(@Nullable Instant lastScheduledExecution, @Nullable Instant lastActualExecution,
@Nullable Instant lastCompletion) {
this(); this();
this.lastScheduledExecutionTime = lastScheduledExecutionTime; this.lastScheduledExecution = lastScheduledExecution;
this.lastActualExecutionTime = lastActualExecutionTime; this.lastActualExecution = lastActualExecution;
this.lastCompletionTime = lastCompletionTime; this.lastCompletion = lastCompletion;
} }
/** /**
@ -69,7 +92,7 @@ public class SimpleTriggerContext implements TriggerContext {
* exposing the given clock. * exposing the given clock.
* @param clock the clock to use for trigger calculation * @param clock the clock to use for trigger calculation
* @since 5.3 * @since 5.3
* @see #update(Date, Date, Date) * @see #update(Instant, Instant, Instant)
*/ */
public SimpleTriggerContext(Clock clock) { public SimpleTriggerContext(Clock clock) {
this.clock = clock; this.clock = clock;
@ -81,11 +104,27 @@ public class SimpleTriggerContext implements TriggerContext {
* @param lastScheduledExecutionTime last <i>scheduled</i> execution time * @param lastScheduledExecutionTime last <i>scheduled</i> execution time
* @param lastActualExecutionTime last <i>actual</i> execution time * @param lastActualExecutionTime last <i>actual</i> execution time
* @param lastCompletionTime last completion time * @param lastCompletionTime last completion time
* @deprecated as of 6.0, in favor of {@link #update(Instant, Instant, Instant)}
*/ */
public void update(Date lastScheduledExecutionTime, Date lastActualExecutionTime, Date lastCompletionTime) { @Deprecated
this.lastScheduledExecutionTime = lastScheduledExecutionTime; public void update(@Nullable Date lastScheduledExecutionTime, @Nullable Date lastActualExecutionTime,
this.lastActualExecutionTime = lastActualExecutionTime; @Nullable Date lastCompletionTime) {
this.lastCompletionTime = lastCompletionTime;
update(toInstant(lastScheduledExecutionTime), toInstant(lastActualExecutionTime), toInstant(lastCompletionTime));
}
/**
* Update this holder's state with the latest time values.
* @param lastScheduledExecution last <i>scheduled</i> execution time
* @param lastActualExecution last <i>actual</i> execution time
* @param lastCompletion last completion time
*/
public void update(@Nullable Instant lastScheduledExecution, @Nullable Instant lastActualExecution,
@Nullable Instant lastCompletion) {
this.lastScheduledExecution = lastScheduledExecution;
this.lastActualExecution = lastActualExecution;
this.lastCompletion = lastCompletion;
} }
@ -96,20 +135,20 @@ public class SimpleTriggerContext implements TriggerContext {
@Override @Override
@Nullable @Nullable
public Date lastScheduledExecutionTime() { public Instant lastScheduledExecution() {
return this.lastScheduledExecutionTime; return this.lastScheduledExecution;
} }
@Override @Override
@Nullable @Nullable
public Date lastActualExecutionTime() { public Instant lastActualExecution() {
return this.lastActualExecutionTime; return this.lastActualExecution;
} }
@Override @Override
@Nullable @Nullable
public Date lastCompletionTime() { public Instant lastCompletion() {
return this.lastCompletionTime; return this.lastCompletion;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,8 +16,10 @@
package org.springframework.scheduling.annotation; package org.springframework.scheduling.annotation;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@ -176,7 +178,7 @@ public class EnableSchedulingTests {
@Override @Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addFixedRateTask(() -> {}, 100); taskRegistrar.addFixedRateTask(() -> {}, Duration.ofMillis(100));
} }
@Bean @Bean
@ -423,7 +425,7 @@ public class EnableSchedulingTests {
taskRegistrar.setScheduler(taskScheduler()); taskRegistrar.setScheduler(taskScheduler());
taskRegistrar.addFixedRateTask(new IntervalTask( taskRegistrar.addFixedRateTask(new IntervalTask(
() -> worker().executedByThread = Thread.currentThread().getName(), () -> worker().executedByThread = Thread.currentThread().getName(),
10, 0)); Duration.ofMillis(10)));
} }
} }
@ -441,7 +443,7 @@ public class EnableSchedulingTests {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.initialize(); scheduler.initialize();
scheduler.schedule(() -> counter().incrementAndGet(), scheduler.schedule(() -> counter().incrementAndGet(),
triggerContext -> new Date(new Date().getTime()+10)); triggerContext -> Instant.now().plus(10, ChronoUnit.MILLIS));
return scheduler; return scheduler;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,13 +22,14 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Calendar; import java.time.Duration;
import java.util.Date; import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@ -118,8 +119,8 @@ class ScheduledAnnotationBeanPostProcessorTests {
Method targetMethod = runnable.getMethod(); Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target); assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedDelay"); assertThat(targetMethod.getName()).isEqualTo("fixedDelay");
assertThat(task.getInitialDelay()).isEqualTo(0L); assertThat(task.getInitialDelayDuration()).isZero();
assertThat(task.getInterval()).isEqualTo(expectedInterval); assertThat(task.getIntervalDuration()).isEqualTo(Duration.ofMillis(expectedInterval));
} }
@ParameterizedTest @ParameterizedTest
@ -152,8 +153,8 @@ class ScheduledAnnotationBeanPostProcessorTests {
assertThat(targetObject).isEqualTo(target); assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate"); assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertSoftly(softly -> { assertSoftly(softly -> {
softly.assertThat(task.getInitialDelay()).as("initial delay").isEqualTo(0); softly.assertThat(task.getInitialDelayDuration()).as("initial delay").isZero();
softly.assertThat(task.getInterval()).as("interval").isEqualTo(expectedInterval); softly.assertThat(task.getIntervalDuration()).as("interval").isEqualTo(Duration.ofMillis(expectedInterval));
}); });
} }
@ -187,8 +188,8 @@ class ScheduledAnnotationBeanPostProcessorTests {
assertThat(targetObject).isEqualTo(target); assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate"); assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertSoftly(softly -> { assertSoftly(softly -> {
softly.assertThat(task.getInitialDelay()).as("initial delay").isEqualTo(expectedInitialDelay); softly.assertThat(task.getInitialDelayDuration()).as("initial delay").isEqualTo(Duration.ofMillis(expectedInitialDelay));
softly.assertThat(task.getInterval()).as("interval").isEqualTo(expectedInterval); softly.assertThat(task.getIntervalDuration()).as("interval").isEqualTo(Duration.ofMillis(expectedInterval));
}); });
} }
@ -251,16 +252,16 @@ class ScheduledAnnotationBeanPostProcessorTests {
Method targetMethod = runnable1.getMethod(); Method targetMethod = runnable1.getMethod();
assertThat(targetObject).isEqualTo(target); assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate"); assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThat(task1.getInitialDelay()).isEqualTo(0); assertThat(task1.getInitialDelayDuration()).isZero();
assertThat(task1.getInterval()).isEqualTo(4_000L); assertThat(task1.getIntervalDuration()).isEqualTo(Duration.ofMillis(4_000L));
IntervalTask task2 = fixedRateTasks.get(1); IntervalTask task2 = fixedRateTasks.get(1);
ScheduledMethodRunnable runnable2 = (ScheduledMethodRunnable) task2.getRunnable(); ScheduledMethodRunnable runnable2 = (ScheduledMethodRunnable) task2.getRunnable();
targetObject = runnable2.getTarget(); targetObject = runnable2.getTarget();
targetMethod = runnable2.getMethod(); targetMethod = runnable2.getMethod();
assertThat(targetObject).isEqualTo(target); assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate"); assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThat(task2.getInitialDelay()).isEqualTo(2_000L); assertThat(task2.getInitialDelayDuration()).isEqualTo(Duration.ofMillis(2_000L));
assertThat(task2.getInterval()).isEqualTo(4_000L); assertThat(task2.getIntervalDuration()).isEqualTo(Duration.ofMillis(4_000L));
} }
@Test @Test
@ -320,20 +321,20 @@ class ScheduledAnnotationBeanPostProcessorTests {
boolean condition = trigger instanceof CronTrigger; boolean condition = trigger instanceof CronTrigger;
assertThat(condition).isTrue(); assertThat(condition).isTrue();
CronTrigger cronTrigger = (CronTrigger) trigger; CronTrigger cronTrigger = (CronTrigger) trigger;
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+10")); ZonedDateTime dateTime = ZonedDateTime.of(2013, 4, 15, 4, 0, 0, 0, ZoneId.of("GMT+10"));
cal.clear(); // Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+10"));
cal.set(2013, 3, 15, 4, 0); // 15-04-2013 4:00 GMT+10 // cal.clear();
Date lastScheduledExecutionTime = cal.getTime(); // cal.set(2013, 3, 15, 4, 0); // 15-04-2013 4:00 GMT+10;
Date lastActualExecutionTime = cal.getTime(); Instant lastScheduledExecution = dateTime.toInstant();
cal.add(Calendar.MINUTE, 30); // 4:30 Instant lastActualExecution = dateTime.toInstant();
Date lastCompletionTime = cal.getTime(); dateTime = dateTime.plusMinutes(30);
Instant lastCompletion = dateTime.toInstant();
TriggerContext triggerContext = new SimpleTriggerContext( TriggerContext triggerContext = new SimpleTriggerContext(
lastScheduledExecutionTime, lastActualExecutionTime, lastCompletionTime); lastScheduledExecution, lastActualExecution, lastCompletion);
cal.add(Calendar.MINUTE, 30); dateTime = dateTime.plusMinutes(90); // 6:00
cal.add(Calendar.HOUR_OF_DAY, 1); // 6:00 Instant nextExecutionTime = cronTrigger.nextExecution(triggerContext);
Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
// assert that 6:00 is next execution time // assert that 6:00 is next execution time
assertThat(nextExecutionTime).isEqualTo(cal.getTime()); assertThat(nextExecutionTime).isEqualTo(dateTime.toInstant());
} }
@Test @Test
@ -407,7 +408,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
Method targetMethod = runnable.getMethod(); Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target); assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("checkForUpdates"); assertThat(targetMethod.getName()).isEqualTo("checkForUpdates");
assertThat(task.getInterval()).isEqualTo(5_000L); assertThat(task.getIntervalDuration()).isEqualTo(Duration.ofMillis(5_000L));
} }
@Test @Test
@ -434,8 +435,8 @@ class ScheduledAnnotationBeanPostProcessorTests {
Method targetMethod = runnable.getMethod(); Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target); assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("checkForUpdates"); assertThat(targetMethod.getName()).isEqualTo("checkForUpdates");
assertThat(task.getInterval()).isEqualTo(5_000L); assertThat(task.getIntervalDuration()).isEqualTo(Duration.ofMillis(5_000L));
assertThat(task.getInitialDelay()).isEqualTo(1_000L); assertThat(task.getInitialDelayDuration()).isEqualTo(Duration.ofMillis(1_000L));
} }
@Test @Test
@ -555,8 +556,8 @@ class ScheduledAnnotationBeanPostProcessorTests {
assertThat(targetObject).isEqualTo(target); assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedDelay"); assertThat(targetMethod.getName()).isEqualTo("fixedDelay");
assertSoftly(softly -> { assertSoftly(softly -> {
softly.assertThat(task.getInitialDelay()).as("initial delay").isEqualTo(expectedInitialDelay); softly.assertThat(task.getInitialDelayDuration()).as("initial delay").isEqualTo(Duration.ofMillis(expectedInitialDelay));
softly.assertThat(task.getInterval()).as("interval").isEqualTo(expectedInterval); softly.assertThat(task.getIntervalDuration()).as("interval").isEqualTo(Duration.ofMillis(expectedInterval));
}); });
} }
@ -599,8 +600,8 @@ class ScheduledAnnotationBeanPostProcessorTests {
assertThat(targetObject).isEqualTo(target); assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate"); assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertSoftly(softly -> { assertSoftly(softly -> {
softly.assertThat(task.getInitialDelay()).as("initial delay").isEqualTo(expectedInitialDelay); softly.assertThat(task.getInitialDelayDuration()).as("initial delay").isEqualTo(Duration.ofMillis(expectedInitialDelay));
softly.assertThat(task.getInterval()).as("interval").isEqualTo(expectedInterval); softly.assertThat(task.getIntervalDuration()).as("interval").isEqualTo(Duration.ofMillis(expectedInterval));
}); });
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
package org.springframework.scheduling.concurrent; package org.springframework.scheduling.concurrent;
import java.time.Instant;
import java.util.Date; import java.util.Date;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -182,11 +183,11 @@ public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutor
} }
@Override @Override
public Date nextExecutionTime(TriggerContext triggerContext) { public Instant nextExecution(TriggerContext triggerContext) {
if (this.actualRunCount.incrementAndGet() > this.maxRunCount) { if (this.actualRunCount.incrementAndGet() > this.maxRunCount) {
return null; return null;
} }
return new Date(); return Instant.now();
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,7 +17,8 @@
package org.springframework.scheduling.config; package org.springframework.scheduling.config;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Date; import java.time.Duration;
import java.time.Instant;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -78,28 +79,28 @@ public class ScheduledTasksBeanDefinitionParserTests {
public void fixedRateTasks() { public void fixedRateTasks() {
List<IntervalTask> tasks = (List<IntervalTask>) new DirectFieldAccessor( List<IntervalTask> tasks = (List<IntervalTask>) new DirectFieldAccessor(
this.registrar).getPropertyValue("fixedRateTasks"); this.registrar).getPropertyValue("fixedRateTasks");
assertThat(tasks.size()).isEqualTo(3); assertThat(tasks).hasSize(3);
assertThat(tasks.get(0).getInterval()).isEqualTo(1000L); assertThat(tasks.get(0).getIntervalDuration()).isEqualTo(Duration.ofMillis(1000L));
assertThat(tasks.get(1).getInterval()).isEqualTo(2000L); assertThat(tasks.get(1).getIntervalDuration()).isEqualTo(Duration.ofMillis(2000L));
assertThat(tasks.get(2).getInterval()).isEqualTo(4000L); assertThat(tasks.get(2).getIntervalDuration()).isEqualTo(Duration.ofMillis(4000L));
assertThat(tasks.get(2).getInitialDelay()).isEqualTo(500); assertThat(tasks.get(2).getInitialDelayDuration()).isEqualTo(Duration.ofMillis(500));
} }
@Test @Test
public void fixedDelayTasks() { public void fixedDelayTasks() {
List<IntervalTask> tasks = (List<IntervalTask>) new DirectFieldAccessor( List<IntervalTask> tasks = (List<IntervalTask>) new DirectFieldAccessor(
this.registrar).getPropertyValue("fixedDelayTasks"); this.registrar).getPropertyValue("fixedDelayTasks");
assertThat(tasks.size()).isEqualTo(2); assertThat(tasks).hasSize(2);
assertThat(tasks.get(0).getInterval()).isEqualTo(3000L); assertThat(tasks.get(0).getIntervalDuration()).isEqualTo(Duration.ofMillis(3000L));
assertThat(tasks.get(1).getInterval()).isEqualTo(3500L); assertThat(tasks.get(1).getIntervalDuration()).isEqualTo(Duration.ofMillis(3500L));
assertThat(tasks.get(1).getInitialDelay()).isEqualTo(250); assertThat(tasks.get(1).getInitialDelayDuration()).isEqualTo(Duration.ofMillis(250));
} }
@Test @Test
public void cronTasks() { public void cronTasks() {
List<CronTask> tasks = (List<CronTask>) new DirectFieldAccessor( List<CronTask> tasks = (List<CronTask>) new DirectFieldAccessor(
this.registrar).getPropertyValue("cronTasks"); this.registrar).getPropertyValue("cronTasks");
assertThat(tasks.size()).isEqualTo(1); assertThat(tasks).hasSize(1);
assertThat(tasks.get(0).getExpression()).isEqualTo("*/4 * 9-17 * * MON-FRI"); assertThat(tasks.get(0).getExpression()).isEqualTo("*/4 * 9-17 * * MON-FRI");
} }
@ -107,7 +108,7 @@ public class ScheduledTasksBeanDefinitionParserTests {
public void triggerTasks() { public void triggerTasks() {
List<TriggerTask> tasks = (List<TriggerTask>) new DirectFieldAccessor( List<TriggerTask> tasks = (List<TriggerTask>) new DirectFieldAccessor(
this.registrar).getPropertyValue("triggerTasks"); this.registrar).getPropertyValue("triggerTasks");
assertThat(tasks.size()).isEqualTo(1); assertThat(tasks).hasSize(1);
assertThat(tasks.get(0).getTrigger()).isInstanceOf(TestTrigger.class); assertThat(tasks.get(0).getTrigger()).isInstanceOf(TestTrigger.class);
} }
@ -122,7 +123,7 @@ public class ScheduledTasksBeanDefinitionParserTests {
static class TestTrigger implements Trigger { static class TestTrigger implements Trigger {
@Override @Override
public Date nextExecutionTime(TriggerContext triggerContext) { public Instant nextExecution(TriggerContext triggerContext) {
return null; return null;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,11 +16,13 @@
package org.springframework.scheduling.support; package org.springframework.scheduling.support;
import java.util.Date; import java.time.Duration;
import java.util.concurrent.TimeUnit; import java.time.Instant;
import java.time.temporal.ChronoUnit;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.TriggerContext; import org.springframework.scheduling.TriggerContext;
import org.springframework.util.NumberUtils; import org.springframework.util.NumberUtils;
@ -34,149 +36,149 @@ public class PeriodicTriggerTests {
@Test @Test
public void fixedDelayFirstExecution() { public void fixedDelayFirstExecution() {
Date now = new Date(); Instant now = Instant.now();
PeriodicTrigger trigger = new PeriodicTrigger(5000); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(5000));
Date next = trigger.nextExecutionTime(context(null, null, null)); Instant next = trigger.nextExecution(context(null, null, null));
assertNegligibleDifference(now, next); assertNegligibleDifference(now, next);
} }
@Test @Test
public void fixedDelayWithInitialDelayFirstExecution() { public void fixedDelayWithInitialDelayFirstExecution() {
Date now = new Date(); Instant now = Instant.now();
long period = 5000; long period = 5000;
long initialDelay = 30000; long initialDelay = 30000;
PeriodicTrigger trigger = new PeriodicTrigger(period); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(period));
trigger.setInitialDelay(initialDelay); trigger.setInitialDelay(initialDelay);
Date next = trigger.nextExecutionTime(context(null, null, null)); Instant next = trigger.nextExecution(context(null, null, null));
assertApproximateDifference(now, next, initialDelay); assertApproximateDifference(now, next, initialDelay);
} }
@Test @Test
public void fixedDelayWithTimeUnitFirstExecution() { public void fixedDelayWithTimeUnitFirstExecution() {
Date now = new Date(); Instant now = Instant.now();
PeriodicTrigger trigger = new PeriodicTrigger(5, TimeUnit.SECONDS); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofSeconds(5));
Date next = trigger.nextExecutionTime(context(null, null, null)); Instant next = trigger.nextExecution(context(null, null, null));
assertNegligibleDifference(now, next); assertNegligibleDifference(now, next);
} }
@Test @Test
public void fixedDelayWithTimeUnitAndInitialDelayFirstExecution() { public void fixedDelayWithTimeUnitAndInitialDelayFirstExecution() {
Date now = new Date(); Instant now = Instant.now();
long period = 5; long period = 5;
long initialDelay = 30; long initialDelay = 30;
PeriodicTrigger trigger = new PeriodicTrigger(period, TimeUnit.SECONDS); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofSeconds(period));
trigger.setInitialDelay(initialDelay); trigger.setInitialDelay(Duration.ofSeconds(initialDelay));
Date next = trigger.nextExecutionTime(context(null, null, null)); Instant next = trigger.nextExecution(context(null, null, null));
assertApproximateDifference(now, next, initialDelay * 1000); assertApproximateDifference(now, next, initialDelay * 1000);
} }
@Test @Test
public void fixedDelaySubsequentExecution() { public void fixedDelaySubsequentExecution() {
Date now = new Date(); Instant now = Instant.now();
long period = 5000; long period = 5000;
PeriodicTrigger trigger = new PeriodicTrigger(period); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(period));
Date next = trigger.nextExecutionTime(context(now, 500, 3000)); Instant next = trigger.nextExecution(context(now, 500, 3000));
assertApproximateDifference(now, next, period + 3000); assertApproximateDifference(now, next, period + 3000);
} }
@Test @Test
public void fixedDelayWithInitialDelaySubsequentExecution() { public void fixedDelayWithInitialDelaySubsequentExecution() {
Date now = new Date(); Instant now = Instant.now();
long period = 5000; long period = 5000;
long initialDelay = 30000; long initialDelay = 30000;
PeriodicTrigger trigger = new PeriodicTrigger(period); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(period));
trigger.setInitialDelay(initialDelay); trigger.setInitialDelay(initialDelay);
Date next = trigger.nextExecutionTime(context(now, 500, 3000)); Instant next = trigger.nextExecution(context(now, 500, 3000));
assertApproximateDifference(now, next, period + 3000); assertApproximateDifference(now, next, period + 3000);
} }
@Test @Test
public void fixedDelayWithTimeUnitSubsequentExecution() { public void fixedDelayWithTimeUnitSubsequentExecution() {
Date now = new Date(); Instant now = Instant.now();
long period = 5; long period = 5;
PeriodicTrigger trigger = new PeriodicTrigger(period, TimeUnit.SECONDS); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofSeconds(period));
Date next = trigger.nextExecutionTime(context(now, 500, 3000)); Instant next = trigger.nextExecution(context(now, 500, 3000));
assertApproximateDifference(now, next, (period * 1000) + 3000); assertApproximateDifference(now, next, (period * 1000) + 3000);
} }
@Test @Test
public void fixedRateFirstExecution() { public void fixedRateFirstExecution() {
Date now = new Date(); Instant now = Instant.now();
PeriodicTrigger trigger = new PeriodicTrigger(5000); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(5000));
trigger.setFixedRate(true); trigger.setFixedRate(true);
Date next = trigger.nextExecutionTime(context(null, null, null)); Instant next = trigger.nextExecution(context(null, null, null));
assertNegligibleDifference(now, next); assertNegligibleDifference(now, next);
} }
@Test @Test
public void fixedRateWithTimeUnitFirstExecution() { public void fixedRateWithTimeUnitFirstExecution() {
Date now = new Date(); Instant now = Instant.now();
PeriodicTrigger trigger = new PeriodicTrigger(5, TimeUnit.SECONDS); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofSeconds(5));
trigger.setFixedRate(true); trigger.setFixedRate(true);
Date next = trigger.nextExecutionTime(context(null, null, null)); Instant next = trigger.nextExecution(context(null, null, null));
assertNegligibleDifference(now, next); assertNegligibleDifference(now, next);
} }
@Test @Test
public void fixedRateWithInitialDelayFirstExecution() { public void fixedRateWithInitialDelayFirstExecution() {
Date now = new Date(); Instant now = Instant.now();
long period = 5000; long period = 5000;
long initialDelay = 30000; long initialDelay = 30000;
PeriodicTrigger trigger = new PeriodicTrigger(period); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(period));
trigger.setFixedRate(true); trigger.setFixedRate(true);
trigger.setInitialDelay(initialDelay); trigger.setInitialDelay(initialDelay);
Date next = trigger.nextExecutionTime(context(null, null, null)); Instant next = trigger.nextExecution(context(null, null, null));
assertApproximateDifference(now, next, initialDelay); assertApproximateDifference(now, next, initialDelay);
} }
@Test @Test
public void fixedRateWithTimeUnitAndInitialDelayFirstExecution() { public void fixedRateWithTimeUnitAndInitialDelayFirstExecution() {
Date now = new Date(); Instant now = Instant.now();
long period = 5; long period = 5;
long initialDelay = 30; long initialDelay = 30;
PeriodicTrigger trigger = new PeriodicTrigger(period, TimeUnit.MINUTES); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMinutes(period));
trigger.setFixedRate(true); trigger.setFixedRate(true);
trigger.setInitialDelay(initialDelay); trigger.setInitialDelay(Duration.ofMinutes(initialDelay));
Date next = trigger.nextExecutionTime(context(null, null, null)); Instant next = trigger.nextExecution(context(null, null, null));
assertApproximateDifference(now, next, (initialDelay * 60 * 1000)); assertApproximateDifference(now, next, (initialDelay * 60 * 1000));
} }
@Test @Test
public void fixedRateSubsequentExecution() { public void fixedRateSubsequentExecution() {
Date now = new Date(); Instant now = Instant.now();
long period = 5000; long period = 5000;
PeriodicTrigger trigger = new PeriodicTrigger(period); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(period));
trigger.setFixedRate(true); trigger.setFixedRate(true);
Date next = trigger.nextExecutionTime(context(now, 500, 3000)); Instant next = trigger.nextExecution(context(now, 500, 3000));
assertApproximateDifference(now, next, period); assertApproximateDifference(now, next, period);
} }
@Test @Test
public void fixedRateWithInitialDelaySubsequentExecution() { public void fixedRateWithInitialDelaySubsequentExecution() {
Date now = new Date(); Instant now = Instant.now();
long period = 5000; long period = 5000;
long initialDelay = 30000; long initialDelay = 30000;
PeriodicTrigger trigger = new PeriodicTrigger(period); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(period));
trigger.setFixedRate(true); trigger.setFixedRate(true);
trigger.setInitialDelay(initialDelay); trigger.setInitialDelay(initialDelay);
Date next = trigger.nextExecutionTime(context(now, 500, 3000)); Instant next = trigger.nextExecution(context(now, 500, 3000));
assertApproximateDifference(now, next, period); assertApproximateDifference(now, next, period);
} }
@Test @Test
public void fixedRateWithTimeUnitSubsequentExecution() { public void fixedRateWithTimeUnitSubsequentExecution() {
Date now = new Date(); Instant now = Instant.now();
long period = 5; long period = 5;
PeriodicTrigger trigger = new PeriodicTrigger(period, TimeUnit.HOURS); PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofHours(period));
trigger.setFixedRate(true); trigger.setFixedRate(true);
Date next = trigger.nextExecutionTime(context(now, 500, 3000)); Instant next = trigger.nextExecution(context(now, 500, 3000));
assertApproximateDifference(now, next, (period * 60 * 60 * 1000)); assertApproximateDifference(now, next, (period * 60 * 60 * 1000));
} }
@Test @Test
public void equalsVerification() { public void equalsVerification() {
PeriodicTrigger trigger1 = new PeriodicTrigger(3000); PeriodicTrigger trigger1 = new PeriodicTrigger(Duration.ofMillis(3000));
PeriodicTrigger trigger2 = new PeriodicTrigger(3000); PeriodicTrigger trigger2 = new PeriodicTrigger(Duration.ofMillis(3000));
assertThat(trigger1.equals(new String("not a trigger"))).isFalse(); assertThat(trigger1.equals(new String("not a trigger"))).isFalse();
assertThat(trigger1.equals(null)).isFalse(); assertThat(trigger1.equals(null)).isFalse();
assertThat(trigger1).isEqualTo(trigger1); assertThat(trigger1).isEqualTo(trigger1);
@ -192,44 +194,46 @@ public class PeriodicTriggerTests {
assertThat(trigger2.equals(trigger1)).isFalse(); assertThat(trigger2.equals(trigger1)).isFalse();
trigger1.setFixedRate(true); trigger1.setFixedRate(true);
assertThat(trigger2).isEqualTo(trigger1); assertThat(trigger2).isEqualTo(trigger1);
PeriodicTrigger trigger3 = new PeriodicTrigger(3, TimeUnit.SECONDS); PeriodicTrigger trigger3 = new PeriodicTrigger(Duration.ofSeconds(3));
trigger3.setInitialDelay(7); trigger3.setInitialDelay(Duration.ofSeconds(7));
trigger3.setFixedRate(true); trigger3.setFixedRate(true);
assertThat(trigger1.equals(trigger3)).isFalse(); assertThat(trigger1.equals(trigger3)).isFalse();
assertThat(trigger3.equals(trigger1)).isFalse(); assertThat(trigger3.equals(trigger1)).isFalse();
trigger1.setInitialDelay(7000); trigger1.setInitialDelay(Duration.ofMillis(7000));
assertThat(trigger3).isEqualTo(trigger1); assertThat(trigger3).isEqualTo(trigger1);
} }
// utility methods // utility methods
private static void assertNegligibleDifference(Date d1, Date d2) { private static void assertNegligibleDifference(Instant d1, @Nullable Instant d2) {
long diff = Math.abs(d1.getTime() - d2.getTime()); assertThat(Duration.between(d1, d2)).isLessThan(Duration.ofMillis(100));
assertThat(diff < 100).as("difference exceeds threshold: " + diff).isTrue();
} }
private static void assertApproximateDifference(Date lesser, Date greater, long expected) { private static void assertApproximateDifference(Instant lesser, Instant greater, long expected) {
long diff = greater.getTime() - lesser.getTime(); long diff = greater.toEpochMilli() - lesser.toEpochMilli();
long variance = Math.abs(expected - diff); long variance = Math.abs(expected - diff);
assertThat(variance < 100).as("expected approximate difference of " + expected + assertThat(variance < 100).as("expected approximate difference of " + expected +
", but actual difference was " + diff).isTrue(); ", but actual difference was " + diff).isTrue();
} }
private static TriggerContext context(Object scheduled, Object actual, Object completion) { private static TriggerContext context(@Nullable Object scheduled, @Nullable Object actual,
return new TestTriggerContext(asDate(scheduled), asDate(actual), asDate(completion)); @Nullable Object completion) {
return new TestTriggerContext(toInstant(scheduled), toInstant(actual), toInstant(completion));
} }
private static Date asDate(Object o) { @Nullable
private static Instant toInstant(@Nullable Object o) {
if (o == null) { if (o == null) {
return null; return null;
} }
if (o instanceof Date) { if (o instanceof Instant) {
return (Date) o; return (Instant) o;
} }
if (o instanceof Number) { if (o instanceof Number) {
return new Date(System.currentTimeMillis() + return Instant.now()
NumberUtils.convertNumberToTargetClass((Number) o, Long.class)); .plus(NumberUtils.convertNumberToTargetClass((Number) o, Long.class),
ChronoUnit.MILLIS);
} }
throw new IllegalArgumentException( throw new IllegalArgumentException(
"expected Date or Number, but actual type was: " + o.getClass()); "expected Date or Number, but actual type was: " + o.getClass());
@ -240,30 +244,35 @@ public class PeriodicTriggerTests {
private static class TestTriggerContext implements TriggerContext { private static class TestTriggerContext implements TriggerContext {
private final Date scheduled; @Nullable
private final Instant scheduled;
private final Date actual; @Nullable
private final Instant actual;
private final Date completion; @Nullable
private final Instant completion;
TestTriggerContext(@Nullable Instant scheduled,
@Nullable Instant actual, @Nullable Instant completion) {
TestTriggerContext(Date scheduled, Date actual, Date completion) {
this.scheduled = scheduled; this.scheduled = scheduled;
this.actual = actual; this.actual = actual;
this.completion = completion; this.completion = completion;
} }
@Override @Override
public Date lastActualExecutionTime() { public Instant lastActualExecution() {
return this.actual; return this.actual;
} }
@Override @Override
public Date lastCompletionTime() { public Instant lastCompletion() {
return this.completion; return this.completion;
} }
@Override @Override
public Date lastScheduledExecutionTime() { public Instant lastScheduledExecution() {
return this.scheduled; return this.scheduled;
} }
} }

View File

@ -17,6 +17,7 @@
package org.springframework.messaging.simp.broker; package org.springframework.messaging.simp.broker;
import java.security.Principal; import java.security.Principal;
import java.time.Duration;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
@ -254,8 +255,8 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
public void startInternal() { public void startInternal() {
publishBrokerAvailableEvent(); publishBrokerAvailableEvent();
if (this.taskScheduler != null) { if (this.taskScheduler != null) {
long interval = initHeartbeatTaskDelay(); Duration interval = initHeartbeatTaskDelay();
if (interval > 0) { if (interval.toMillis() > 0) {
this.heartbeatFuture = this.taskScheduler.scheduleWithFixedDelay(new HeartbeatTask(), interval); this.heartbeatFuture = this.taskScheduler.scheduleWithFixedDelay(new HeartbeatTask(), interval);
} }
} }
@ -266,15 +267,15 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
} }
} }
private long initHeartbeatTaskDelay() { private Duration initHeartbeatTaskDelay() {
if (getHeartbeatValue() == null) { if (getHeartbeatValue() == null) {
return 0; return Duration.ZERO;
} }
else if (getHeartbeatValue()[0] > 0 && getHeartbeatValue()[1] > 0) { else if (getHeartbeatValue()[0] > 0 && getHeartbeatValue()[1] > 0) {
return Math.min(getHeartbeatValue()[0], getHeartbeatValue()[1]); return Duration.ofMillis(Math.min(getHeartbeatValue()[0], getHeartbeatValue()[1]));
} }
else { else {
return (getHeartbeatValue()[0] > 0 ? getHeartbeatValue()[0] : getHeartbeatValue()[1]); return Duration.ofMillis(getHeartbeatValue()[0] > 0 ? getHeartbeatValue()[0] : getHeartbeatValue()[1]);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,9 +17,9 @@
package org.springframework.messaging.simp.stomp; package org.springframework.messaging.simp.stomp;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -566,7 +566,7 @@ public class DefaultStompSession implements ConnectionHandlingStompSession {
private void initReceiptHandling() { private void initReceiptHandling() {
Assert.notNull(getTaskScheduler(), "To track receipts, a TaskScheduler must be configured"); Assert.notNull(getTaskScheduler(), "To track receipts, a TaskScheduler must be configured");
DefaultStompSession.this.receiptHandlers.put(this.receiptId, this); DefaultStompSession.this.receiptHandlers.put(this.receiptId, this);
Date startTime = new Date(System.currentTimeMillis() + getReceiptTimeLimit()); Instant startTime = Instant.now().plusMillis(getReceiptTimeLimit());
this.future = getTaskScheduler().schedule(this::handleReceiptNotReceived, startTime); this.future = getTaskScheduler().schedule(this::handleReceiptNotReceived, startTime);
} }

View File

@ -17,6 +17,7 @@
package org.springframework.messaging.simp.stomp; package org.springframework.messaging.simp.stomp;
import java.security.Principal; import java.security.Principal;
import java.time.Duration;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -457,7 +458,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
this.tcpClient.connect(handler, new FixedIntervalReconnectStrategy(5000)); this.tcpClient.connect(handler, new FixedIntervalReconnectStrategy(5000));
if (this.taskScheduler != null) { if (this.taskScheduler != null) {
this.taskScheduler.scheduleWithFixedDelay(new ClientSendMessageCountTask(), 5000); this.taskScheduler.scheduleWithFixedDelay(new ClientSendMessageCountTask(), Duration.ofMillis(5000));
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
package org.springframework.messaging.simp.user; package org.springframework.messaging.simp.user;
import java.time.Duration;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -111,7 +112,7 @@ public class UserRegistryMessageHandler implements MessageHandler, ApplicationLi
@Override @Override
public void onApplicationEvent(BrokerAvailabilityEvent event) { public void onApplicationEvent(BrokerAvailabilityEvent event) {
if (event.isBrokerAvailable()) { if (event.isBrokerAvailable()) {
long delay = getRegistryExpirationPeriod() / 2; Duration delay = Duration.ofMillis(getRegistryExpirationPeriod() / 2);
this.scheduledFuture = this.scheduler.scheduleWithFixedDelay(this.schedulerTask, delay); this.scheduledFuture = this.scheduler.scheduleWithFixedDelay(this.schedulerTask, delay);
} }
else { else {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package org.springframework.messaging.simp.broker; package org.springframework.messaging.simp.broker;
import java.security.Principal; import java.security.Principal;
import java.time.Duration;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
@ -184,13 +185,13 @@ public class SimpleBrokerMessageHandlerTests {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public void startAndStopWithHeartbeatValue() { public void startAndStopWithHeartbeatValue() {
ScheduledFuture future = mock(ScheduledFuture.class); ScheduledFuture future = mock(ScheduledFuture.class);
given(this.taskScheduler.scheduleWithFixedDelay(any(Runnable.class), eq(15000L))).willReturn(future); given(this.taskScheduler.scheduleWithFixedDelay(any(Runnable.class), eq(Duration.ofMillis(15000)))).willReturn(future);
this.messageHandler.setTaskScheduler(this.taskScheduler); this.messageHandler.setTaskScheduler(this.taskScheduler);
this.messageHandler.setHeartbeatValue(new long[] {15000, 16000}); this.messageHandler.setHeartbeatValue(new long[] {15000, 16000});
this.messageHandler.start(); this.messageHandler.start();
verify(this.taskScheduler).scheduleWithFixedDelay(any(Runnable.class), eq(15000L)); verify(this.taskScheduler).scheduleWithFixedDelay(any(Runnable.class), eq(Duration.ofMillis(15000)));
verifyNoMoreInteractions(this.taskScheduler, future); verifyNoMoreInteractions(this.taskScheduler, future);
this.messageHandler.stop(); this.messageHandler.stop();
@ -205,7 +206,7 @@ public class SimpleBrokerMessageHandlerTests {
this.messageHandler.setHeartbeatValue(new long[] {0, 10000}); this.messageHandler.setHeartbeatValue(new long[] {0, 10000});
this.messageHandler.start(); this.messageHandler.start();
verify(this.taskScheduler).scheduleWithFixedDelay(any(Runnable.class), eq(10000L)); verify(this.taskScheduler).scheduleWithFixedDelay(any(Runnable.class), eq(Duration.ofMillis(10000)));
} }
@Test @Test
@ -215,7 +216,7 @@ public class SimpleBrokerMessageHandlerTests {
this.messageHandler.start(); this.messageHandler.start();
ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class); ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(this.taskScheduler).scheduleWithFixedDelay(taskCaptor.capture(), eq(1L)); verify(this.taskScheduler).scheduleWithFixedDelay(taskCaptor.capture(), eq(Duration.ofMillis(1)));
Runnable heartbeatTask = taskCaptor.getValue(); Runnable heartbeatTask = taskCaptor.getValue();
assertThat(heartbeatTask).isNotNull(); assertThat(heartbeatTask).isNotNull();
@ -246,7 +247,7 @@ public class SimpleBrokerMessageHandlerTests {
this.messageHandler.start(); this.messageHandler.start();
ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class); ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(this.taskScheduler).scheduleWithFixedDelay(taskCaptor.capture(), eq(1L)); verify(this.taskScheduler).scheduleWithFixedDelay(taskCaptor.capture(), eq(Duration.ofMillis(1)));
Runnable heartbeatTask = taskCaptor.getValue(); Runnable heartbeatTask = taskCaptor.getValue();
assertThat(heartbeatTask).isNotNull(); assertThat(heartbeatTask).isNotNull();
@ -277,7 +278,7 @@ public class SimpleBrokerMessageHandlerTests {
this.messageHandler.start(); this.messageHandler.start();
ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class); ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(this.taskScheduler).scheduleWithFixedDelay(taskCaptor.capture(), eq(1L)); verify(this.taskScheduler).scheduleWithFixedDelay(taskCaptor.capture(), eq(Duration.ofMillis(1)));
Runnable heartbeatTask = taskCaptor.getValue(); Runnable heartbeatTask = taskCaptor.getValue();
assertThat(heartbeatTask).isNotNull(); assertThat(heartbeatTask).isNotNull();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,8 +17,8 @@
package org.springframework.messaging.simp.stomp; package org.springframework.messaging.simp.stomp;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -628,7 +628,7 @@ public class DefaultStompSessionTests {
AtomicReference<Boolean> notReceived = new AtomicReference<>(); AtomicReference<Boolean> notReceived = new AtomicReference<>();
ScheduledFuture future = mock(ScheduledFuture.class); ScheduledFuture future = mock(ScheduledFuture.class);
given(taskScheduler.schedule(any(Runnable.class), any(Date.class))).willReturn(future); given(taskScheduler.schedule(any(Runnable.class), any(Instant.class))).willReturn(future);
StompHeaders headers = new StompHeaders(); StompHeaders headers = new StompHeaders();
headers.setDestination("/topic/foo"); headers.setDestination("/topic/foo");
@ -637,7 +637,7 @@ public class DefaultStompSessionTests {
receiptable.addReceiptLostTask(() -> notReceived.set(true)); receiptable.addReceiptLostTask(() -> notReceived.set(true));
ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class); ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(taskScheduler).schedule(taskCaptor.capture(), (Date) notNull()); verify(taskScheduler).schedule(taskCaptor.capture(), (Instant) notNull());
Runnable scheduledTask = taskCaptor.getValue(); Runnable scheduledTask = taskCaptor.getValue();
assertThat(scheduledTask).isNotNull(); assertThat(scheduledTask).isNotNull();

View File

@ -16,6 +16,7 @@
package org.springframework.messaging.simp.stomp; package org.springframework.messaging.simp.stomp;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -166,7 +167,7 @@ class StompBrokerRelayMessageHandlerTests {
this.tcpClient.handleMessage(MessageBuilder.createMessage(new byte[0], accessor.getMessageHeaders())); this.tcpClient.handleMessage(MessageBuilder.createMessage(new byte[0], accessor.getMessageHeaders()));
// Run the messageCountTask to clear the message count // Run the messageCountTask to clear the message count
verify(this.brokerRelay.getTaskScheduler()).scheduleWithFixedDelay(this.messageCountTaskCaptor.capture(), eq(5000L)); verify(this.brokerRelay.getTaskScheduler()).scheduleWithFixedDelay(this.messageCountTaskCaptor.capture(), eq(Duration.ofMillis(5000L)));
this.messageCountTaskCaptor.getValue().run(); this.messageCountTaskCaptor.getValue().run();
accessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE); accessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
package org.springframework.messaging.simp.user; package org.springframework.messaging.simp.user;
import java.time.Duration;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -94,7 +95,7 @@ public class UserRegistryMessageHandlerTests {
public void brokerUnavailableEvent() throws Exception { public void brokerUnavailableEvent() throws Exception {
ScheduledFuture future = mock(ScheduledFuture.class); ScheduledFuture future = mock(ScheduledFuture.class);
given(this.taskScheduler.scheduleWithFixedDelay(any(Runnable.class), any(Long.class))).willReturn(future); given(this.taskScheduler.scheduleWithFixedDelay(any(Runnable.class), any(Duration.class))).willReturn(future);
BrokerAvailabilityEvent event = new BrokerAvailabilityEvent(true, this); BrokerAvailabilityEvent event = new BrokerAvailabilityEvent(true, this);
this.handler.onApplicationEvent(event); this.handler.onApplicationEvent(event);
@ -181,7 +182,7 @@ public class UserRegistryMessageHandlerTests {
this.handler.onApplicationEvent(event); this.handler.onApplicationEvent(event);
ArgumentCaptor<? extends Runnable> captor = ArgumentCaptor.forClass(Runnable.class); ArgumentCaptor<? extends Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
verify(this.taskScheduler).scheduleWithFixedDelay(captor.capture(), eq(10000L)); verify(this.taskScheduler).scheduleWithFixedDelay(captor.capture(), eq(Duration.ofMillis(10000L)));
return captor.getValue(); return captor.getValue();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,6 +19,7 @@ package org.springframework.web.socket.messaging;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -403,6 +404,7 @@ public class WebSocketStompClient extends StompClientSupport implements SmartLif
public void onReadInactivity(final Runnable runnable, final long duration) { public void onReadInactivity(final Runnable runnable, final long duration) {
Assert.state(getTaskScheduler() != null, "No TaskScheduler configured"); Assert.state(getTaskScheduler() != null, "No TaskScheduler configured");
this.lastReadTime = System.currentTimeMillis(); this.lastReadTime = System.currentTimeMillis();
Duration delay = Duration.ofMillis(duration / 2);
this.inactivityTasks.add(getTaskScheduler().scheduleWithFixedDelay(() -> { this.inactivityTasks.add(getTaskScheduler().scheduleWithFixedDelay(() -> {
if (System.currentTimeMillis() - this.lastReadTime > duration) { if (System.currentTimeMillis() - this.lastReadTime > duration) {
try { try {
@ -414,13 +416,14 @@ public class WebSocketStompClient extends StompClientSupport implements SmartLif
} }
} }
} }
}, duration / 2)); }, delay));
} }
@Override @Override
public void onWriteInactivity(final Runnable runnable, final long duration) { public void onWriteInactivity(final Runnable runnable, final long duration) {
Assert.state(getTaskScheduler() != null, "No TaskScheduler configured"); Assert.state(getTaskScheduler() != null, "No TaskScheduler configured");
this.lastWriteTime = System.currentTimeMillis(); this.lastWriteTime = System.currentTimeMillis();
Duration delay = Duration.ofMillis(duration / 2);
this.inactivityTasks.add(getTaskScheduler().scheduleWithFixedDelay(() -> { this.inactivityTasks.add(getTaskScheduler().scheduleWithFixedDelay(() -> {
if (System.currentTimeMillis() - this.lastWriteTime > duration) { if (System.currentTimeMillis() - this.lastWriteTime > duration) {
try { try {
@ -432,7 +435,7 @@ public class WebSocketStompClient extends StompClientSupport implements SmartLif
} }
} }
} }
}, duration / 2)); }, delay));
} }
@Override @Override

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,8 +18,9 @@ package org.springframework.web.socket.sockjs.client;
import java.net.URI; import java.net.URI;
import java.security.Principal; import java.security.Principal;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -160,7 +161,7 @@ class DefaultTransportRequest implements TransportRequest {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Scheduling connect to time out after " + this.timeoutValue + " ms."); logger.trace("Scheduling connect to time out after " + this.timeoutValue + " ms.");
} }
Date timeoutDate = new Date(System.currentTimeMillis() + this.timeoutValue); Instant timeoutDate = Instant.now().plus(this.timeoutValue, ChronoUnit.MILLIS);
this.timeoutScheduler.schedule(connectHandler, timeoutDate); this.timeoutScheduler.schedule(connectHandler, timeoutDate);
} }
else if (logger.isTraceEnabled()) { else if (logger.isTraceEnabled()) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.web.socket.sockjs.transport;
import java.io.IOException; import java.io.IOException;
import java.security.Principal; import java.security.Principal;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -380,6 +381,7 @@ public class TransportHandlingSockJsService extends AbstractSockJsService implem
if (this.sessionCleanupTask != null) { if (this.sessionCleanupTask != null) {
return; return;
} }
Duration disconnectDelay = Duration.ofMillis(getDisconnectDelay());
this.sessionCleanupTask = getTaskScheduler().scheduleAtFixedRate(() -> { this.sessionCleanupTask = getTaskScheduler().scheduleAtFixedRate(() -> {
List<String> removedIds = new ArrayList<>(); List<String> removedIds = new ArrayList<>();
for (SockJsSession session : this.sessions.values()) { for (SockJsSession session : this.sessions.values()) {
@ -398,7 +400,7 @@ public class TransportHandlingSockJsService extends AbstractSockJsService implem
if (logger.isDebugEnabled() && !removedIds.isEmpty()) { if (logger.isDebugEnabled() && !removedIds.isEmpty()) {
logger.debug("Closed " + removedIds.size() + " sessions: " + removedIds); logger.debug("Closed " + removedIds.size() + " sessions: " + removedIds);
} }
}, getDisconnectDelay()); }, disconnectDelay);
} }
} }

View File

@ -17,9 +17,10 @@
package org.springframework.web.socket.sockjs.transport.session; package org.springframework.web.socket.sockjs.transport.session;
import java.io.IOException; import java.io.IOException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -270,7 +271,7 @@ public abstract class AbstractSockJsSession implements SockJsSession {
if (!isActive()) { if (!isActive()) {
return; return;
} }
Date time = new Date(System.currentTimeMillis() + this.config.getHeartbeatTime()); Instant time = Instant.now().plus(this.config.getHeartbeatTime(), ChronoUnit.MILLIS);
this.heartbeatTask = new HeartbeatTask(); this.heartbeatTask = new HeartbeatTask();
this.heartbeatFuture = this.config.getTaskScheduler().schedule(this.heartbeatTask, time); this.heartbeatFuture = this.config.getTaskScheduler().schedule(this.heartbeatTask, time);
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,7 +18,8 @@ package org.springframework.web.socket.config;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Date; import java.time.Duration;
import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
@ -320,27 +321,27 @@ class TestTaskScheduler implements TaskScheduler {
} }
@Override @Override
public ScheduledFuture schedule(Runnable task, Date startTime) { public ScheduledFuture schedule(Runnable task, Instant startTime) {
return null; return null;
} }
@Override @Override
public ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period) { public ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) {
return null; return null;
} }
@Override @Override
public ScheduledFuture scheduleAtFixedRate(Runnable task, long period) { public ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period) {
return null; return null;
} }
@Override @Override
public ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay) { public ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) {
return null; return null;
} }
@Override @Override
public ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay) { public ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay) {
return null; return null;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.web.socket.messaging;
import java.net.URI; import java.net.URI;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -295,7 +296,7 @@ public class WebSocketStompClientTests {
TcpConnection<byte[]> tcpConnection = getTcpConnection(); TcpConnection<byte[]> tcpConnection = getTcpConnection();
ScheduledFuture future = mock(ScheduledFuture.class); ScheduledFuture future = mock(ScheduledFuture.class);
given(this.taskScheduler.scheduleWithFixedDelay(any(), eq(1L))).willReturn(future); given(this.taskScheduler.scheduleWithFixedDelay(any(), eq(Duration.ofMillis(1)))).willReturn(future);
tcpConnection.onReadInactivity(mock(Runnable.class), 2L); tcpConnection.onReadInactivity(mock(Runnable.class), 2L);
tcpConnection.onWriteInactivity(mock(Runnable.class), 2L); tcpConnection.onWriteInactivity(mock(Runnable.class), 2L);
@ -332,7 +333,7 @@ public class WebSocketStompClientTests {
throws InterruptedException { throws InterruptedException {
ArgumentCaptor<Runnable> inactivityTaskCaptor = ArgumentCaptor.forClass(Runnable.class); ArgumentCaptor<Runnable> inactivityTaskCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(this.taskScheduler).scheduleWithFixedDelay(inactivityTaskCaptor.capture(), eq(delay/2)); verify(this.taskScheduler).scheduleWithFixedDelay(inactivityTaskCaptor.capture(), eq(Duration.ofMillis(delay/2)));
verifyNoMoreInteractions(this.taskScheduler); verifyNoMoreInteractions(this.taskScheduler);
if (sleepTime > 0) { if (sleepTime > 0) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,7 +18,7 @@ package org.springframework.web.socket.sockjs.client;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.Date; import java.time.Instant;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -115,7 +115,7 @@ public class DefaultTransportRequestTests {
// Get and invoke the scheduled timeout task // Get and invoke the scheduled timeout task
ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class); ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(scheduler).schedule(taskCaptor.capture(), any(Date.class)); verify(scheduler).schedule(taskCaptor.capture(), any(Instant.class));
verifyNoMoreInteractions(scheduler); verifyNoMoreInteractions(scheduler);
taskCaptor.getValue().run(); taskCaptor.getValue().run();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
package org.springframework.web.socket.sockjs.transport.handler; package org.springframework.web.socket.sockjs.transport.handler;
import java.time.Duration;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -153,7 +154,7 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
assertThat(this.servletResponse.getStatus()).isEqualTo(200); assertThat(this.servletResponse.getStatus()).isEqualTo(200);
verify(this.xhrHandler).handleRequest(this.request, this.response, this.wsHandler, this.session); verify(this.xhrHandler).handleRequest(this.request, this.response, this.wsHandler, this.session);
verify(taskScheduler).scheduleAtFixedRate(any(Runnable.class), eq(service.getDisconnectDelay())); verify(taskScheduler).scheduleAtFixedRate(any(Runnable.class), eq(Duration.ofMillis(this.service.getDisconnectDelay())));
assertThat(this.response.getHeaders().getCacheControl()).isEqualTo("no-store, no-cache, must-revalidate, max-age=0"); assertThat(this.response.getHeaders().getCacheControl()).isEqualTo("no-store, no-cache, must-revalidate, max-age=0");
assertThat(this.servletResponse.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isNull(); assertThat(this.servletResponse.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isNull();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
package org.springframework.web.socket.sockjs.transport.handler; package org.springframework.web.socket.sockjs.transport.handler;
import java.util.Date; import java.time.Instant;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -81,7 +81,7 @@ public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests
transportHandler.handleRequest(this.request, this.response, this.webSocketHandler, session); transportHandler.handleRequest(this.request, this.response, this.webSocketHandler, session);
assertThat(this.servletRequest.isAsyncStarted()).as("Polling request should remain open").isTrue(); assertThat(this.servletRequest.isAsyncStarted()).as("Polling request should remain open").isTrue();
verify(this.taskScheduler).schedule(any(Runnable.class), any(Date.class)); verify(this.taskScheduler).schedule(any(Runnable.class), any(Instant.class));
resetRequestAndResponse(); resetRequestAndResponse();
transportHandler.handleRequest(this.request, this.response, this.webSocketHandler, session); transportHandler.handleRequest(this.request, this.response, this.webSocketHandler, session);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,8 +17,8 @@
package org.springframework.web.socket.sockjs.transport.session; package org.springframework.web.socket.sockjs.transport.session;
import java.io.IOException; import java.io.IOException;
import java.time.Instant;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -262,7 +262,7 @@ public class SockJsSessionTests extends AbstractSockJsSessionTests<TestSockJsSes
assertThat(this.session.getSockJsFramesWritten().size()).isEqualTo(1); assertThat(this.session.getSockJsFramesWritten().size()).isEqualTo(1);
assertThat(this.session.getSockJsFramesWritten().get(0)).isEqualTo(SockJsFrame.heartbeatFrame()); assertThat(this.session.getSockJsFramesWritten().get(0)).isEqualTo(SockJsFrame.heartbeatFrame());
verify(this.taskScheduler).schedule(any(Runnable.class), any(Date.class)); verify(this.taskScheduler).schedule(any(Runnable.class), any(Instant.class));
verifyNoMoreInteractions(this.taskScheduler); verifyNoMoreInteractions(this.taskScheduler);
} }
@ -286,12 +286,12 @@ public class SockJsSessionTests extends AbstractSockJsSessionTests<TestSockJsSes
@Test @Test
public void scheduleAndCancelHeartbeat() { public void scheduleAndCancelHeartbeat() {
ScheduledFuture<?> task = mock(ScheduledFuture.class); ScheduledFuture<?> task = mock(ScheduledFuture.class);
willReturn(task).given(this.taskScheduler).schedule(any(Runnable.class), any(Date.class)); willReturn(task).given(this.taskScheduler).schedule(any(Runnable.class), any(Instant.class));
this.session.setActive(true); this.session.setActive(true);
this.session.scheduleHeartbeat(); this.session.scheduleHeartbeat();
verify(this.taskScheduler).schedule(any(Runnable.class), any(Date.class)); verify(this.taskScheduler).schedule(any(Runnable.class), any(Instant.class));
verifyNoMoreInteractions(this.taskScheduler); verifyNoMoreInteractions(this.taskScheduler);
given(task.isCancelled()).willReturn(false); given(task.isCancelled()).willReturn(false);

View File

@ -3836,27 +3836,17 @@ The following listing shows the `TaskScheduler` interface definition:
ScheduledFuture schedule(Runnable task, Instant startTime); ScheduledFuture schedule(Runnable task, Instant startTime);
ScheduledFuture schedule(Runnable task, Date startTime);
ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period); ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period);
ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);
ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period); ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period);
ScheduledFuture scheduleAtFixedRate(Runnable task, long period);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay); ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay); ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
}
---- ----
The simplest method is the one named `schedule` that takes only a `Runnable` and a `Date`. The simplest method is the one named `schedule` that takes only a `Runnable` and an `Instant`.
That causes the task to run once after the specified time. All of the other methods That causes the task to run once after the specified time. All of the other methods
are capable of scheduling tasks to run repeatedly. The fixed-rate and fixed-delay are capable of scheduling tasks to run repeatedly. The fixed-rate and fixed-delay
methods are for simple, periodic execution, but the method that accepts a `Trigger` is methods are for simple, periodic execution, but the method that accepts a `Trigger` is