From 9b739a2310bac781c89a22bf92650615f9dd7406 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Tue, 5 Jul 2022 10:32:47 +0200 Subject: [PATCH] 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 --- .../scheduling/TaskScheduler.java | 57 ++++--- .../springframework/scheduling/Trigger.java | 21 ++- .../scheduling/TriggerContext.java | 46 +++++- .../ScheduledAnnotationBeanPostProcessor.java | 34 ++-- .../concurrent/ConcurrentTaskScheduler.java | 97 ++++++++--- .../concurrent/ReschedulingRunnable.java | 17 +- .../concurrent/ThreadPoolTaskScheduler.java | 29 ++-- .../scheduling/config/FixedDelayTask.java | 22 ++- .../scheduling/config/FixedRateTask.java | 23 ++- .../scheduling/config/IntervalTask.java | 80 +++++++++- .../config/ScheduledTaskRegistrar.java | 115 ++++++------- .../scheduling/support/CronTrigger.java | 22 +-- .../scheduling/support/PeriodicTrigger.java | 141 +++++++++++++--- .../support/SimpleTriggerContext.java | 77 ++++++--- .../annotation/EnableSchedulingTests.java | 12 +- ...duledAnnotationBeanPostProcessorTests.java | 67 ++++---- .../ThreadPoolTaskSchedulerTests.java | 7 +- ...heduledTasksBeanDefinitionParserTests.java | 29 ++-- .../support/PeriodicTriggerTests.java | 151 ++++++++++-------- .../broker/SimpleBrokerMessageHandler.java | 13 +- .../simp/stomp/DefaultStompSession.java | 6 +- .../stomp/StompBrokerRelayMessageHandler.java | 3 +- .../simp/user/UserRegistryMessageHandler.java | 5 +- .../SimpleBrokerMessageHandlerTests.java | 15 +- .../simp/stomp/DefaultStompSessionTests.java | 8 +- .../StompBrokerRelayMessageHandlerTests.java | 3 +- .../user/UserRegistryMessageHandlerTests.java | 7 +- .../messaging/WebSocketStompClient.java | 9 +- .../client/DefaultTransportRequest.java | 7 +- .../TransportHandlingSockJsService.java | 6 +- .../session/AbstractSockJsSession.java | 5 +- .../HandlersBeanDefinitionParserTests.java | 15 +- .../messaging/WebSocketStompClientTests.java | 7 +- .../client/DefaultTransportRequestTests.java | 6 +- .../handler/DefaultSockJsServiceTests.java | 5 +- .../HttpSendingTransportHandlerTests.java | 6 +- .../transport/session/SockJsSessionTests.java | 10 +- src/docs/asciidoc/integration.adoc | 12 +- 38 files changed, 787 insertions(+), 408 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/TaskScheduler.java b/spring-context/src/main/java/org/springframework/scheduling/TaskScheduler.java index ad81312237e7..79be3a557299 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/TaskScheduler.java +++ b/spring-context/src/main/java/org/springframework/scheduling/TaskScheduler.java @@ -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"); * you may not use this file except in compliance with the License. @@ -89,11 +89,8 @@ default Clock getClock() { * @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) * @since 5.0 - * @see #schedule(Runnable, Date) */ - default ScheduledFuture schedule(Runnable task, Instant startTime) { - return schedule(task, Date.from(startTime)); - } + ScheduledFuture schedule(Runnable task, Instant startTime); /** * Schedule the given {@link Runnable}, invoking it at the specified execution time. @@ -105,8 +102,12 @@ default ScheduledFuture schedule(Runnable task, Instant startTime) { * @return a {@link ScheduledFuture} representing pending completion of the task * @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) + * @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 @@ -121,11 +122,8 @@ default ScheduledFuture schedule(Runnable task, Instant startTime) { * @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) * @since 5.0 - * @see #scheduleAtFixedRate(Runnable, Date, long) */ - default ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) { - return scheduleAtFixedRate(task, Date.from(startTime), period.toMillis()); - } + ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period); /** * Schedule the given {@link Runnable}, invoking it at the specified execution time @@ -139,8 +137,12 @@ default ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, * @return a {@link ScheduledFuture} representing pending completion of the task * @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) + * @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 @@ -153,11 +155,8 @@ default ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, * @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) * @since 5.0 - * @see #scheduleAtFixedRate(Runnable, long) */ - default ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period) { - return scheduleAtFixedRate(task, period.toMillis()); - } + ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period); /** * Schedule the given {@link Runnable}, starting as soon as possible and @@ -169,8 +168,12 @@ default ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period) { * @return a {@link ScheduledFuture} representing pending completion of the task * @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) + * @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 @@ -186,11 +189,8 @@ default ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period) { * @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) * @since 5.0 - * @see #scheduleWithFixedDelay(Runnable, Date, long) */ - default ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) { - return scheduleWithFixedDelay(task, Date.from(startTime), delay.toMillis()); - } + ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay); /** * Schedule the given {@link Runnable}, invoking it at the specified execution time @@ -206,8 +206,12 @@ default ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTi * @return a {@link ScheduledFuture} representing pending completion of the task * @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) + * @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 @@ -220,11 +224,8 @@ default ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTi * @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) * @since 5.0 - * @see #scheduleWithFixedDelay(Runnable, long) */ - default ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay) { - return scheduleWithFixedDelay(task, delay.toMillis()); - } + ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay); /** * Schedule the given {@link Runnable}, starting as soon as possible and invoking it with @@ -237,7 +238,11 @@ default ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay) * @return a {@link ScheduledFuture} representing pending completion of the task * @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) + * @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)); + } } diff --git a/spring-context/src/main/java/org/springframework/scheduling/Trigger.java b/spring-context/src/main/java/org/springframework/scheduling/Trigger.java index 16534514dc28..baf21776aff1 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/Trigger.java +++ b/spring-context/src/main/java/org/springframework/scheduling/Trigger.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.scheduling; +import java.time.Instant; import java.util.Date; import org.springframework.lang.Nullable; @@ -37,8 +38,24 @@ public interface Trigger { * and last completion time * @return the next execution time as defined by the trigger, * or {@code null} if the trigger won't fire anymore + * @deprecated as of 6.0, in favor of {@link #nextExecution(TriggerContext)} */ + @Deprecated @Nullable - Date nextExecutionTime(TriggerContext triggerContext); + 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 + Instant nextExecution(TriggerContext triggerContext); } diff --git a/spring-context/src/main/java/org/springframework/scheduling/TriggerContext.java b/spring-context/src/main/java/org/springframework/scheduling/TriggerContext.java index f50421eaa17d..933c1dde16da 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/TriggerContext.java +++ b/spring-context/src/main/java/org/springframework/scheduling/TriggerContext.java @@ -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"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.scheduling; import java.time.Clock; +import java.time.Instant; import java.util.Date; import org.springframework.lang.Nullable; @@ -43,22 +44,59 @@ default Clock getClock() { /** * Return the last scheduled execution time of the task, * or {@code null} if not scheduled before. + * @deprecated as of 6.0, in favor on {@link #lastScheduledExecution()} */ @Nullable - Date lastScheduledExecutionTime(); + @Deprecated + default Date lastScheduledExecutionTime() { + Instant instant = lastScheduledExecution(); + return instant != null ? Date.from(instant) : null; + } + + /** + * Return the last scheduled execution time of the task, + * or {@code null} if not scheduled before. + * @since 6.0 + */ + @Nullable + Instant lastScheduledExecution(); /** * Return the last actual execution time of the task, * or {@code null} if not scheduled before. + * @deprecated as of 6.0, in favor on {@link #lastActualExecution()} */ + @Deprecated @Nullable - Date lastActualExecutionTime(); + default Date lastActualExecutionTime() { + Instant instant = lastActualExecution(); + return instant != null ? Date.from(instant) : null; + } + + /** + * Return the last actual execution time of the task, + * or {@code null} if not scheduled before. + */ + @Nullable + 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, * or {@code null} if not scheduled before. */ @Nullable - Date lastCompletionTime(); + Instant lastCompletion(); } diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java index 23eb0de53b5b..f11feca15852 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java @@ -405,16 +405,16 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Set tasks = new LinkedHashSet<>(4); // Determine initial delay - long initialDelay = convertToMillis(scheduled.initialDelay(), scheduled.timeUnit()); + Duration initialDelay = toDuration(scheduled.initialDelay(), scheduled.timeUnit()); String initialDelayString = scheduled.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) { initialDelayString = this.embeddedValueResolver.resolveStringValue(initialDelayString); } if (StringUtils.hasLength(initialDelayString)) { try { - initialDelay = convertToMillis(initialDelayString, scheduled.timeUnit()); + initialDelay = toDuration(initialDelayString, scheduled.timeUnit()); } catch (RuntimeException ex) { throw new IllegalArgumentException( @@ -432,7 +432,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) zone = this.embeddedValueResolver.resolveStringValue(zone); } 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; if (!Scheduled.CRON_DISABLED.equals(cron)) { TimeZone timeZone; @@ -448,13 +448,13 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } // At this point we don't need to differentiate between initial delay set or not anymore - if (initialDelay < 0) { - initialDelay = 0; + if (initialDelay.isNegative()) { + initialDelay = Duration.ZERO; } // Check fixed delay - long fixedDelay = convertToMillis(scheduled.fixedDelay(), scheduled.timeUnit()); - if (fixedDelay >= 0) { + Duration fixedDelay = toDuration(scheduled.fixedDelay(), scheduled.timeUnit()); + if (!fixedDelay.isNegative()) { Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay))); @@ -469,7 +469,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; try { - fixedDelay = convertToMillis(fixedDelayString, scheduled.timeUnit()); + fixedDelay = toDuration(fixedDelayString, scheduled.timeUnit()); } catch (RuntimeException ex) { throw new IllegalArgumentException( @@ -480,8 +480,8 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) } // Check fixed rate - long fixedRate = convertToMillis(scheduled.fixedRate(), scheduled.timeUnit()); - if (fixedRate >= 0) { + Duration fixedRate = toDuration(scheduled.fixedRate(), scheduled.timeUnit()); + if (!fixedRate.isNegative()) { Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay))); @@ -495,7 +495,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; try { - fixedRate = convertToMillis(fixedRateString, scheduled.timeUnit()); + fixedRate = toDuration(fixedRateString, scheduled.timeUnit()); } catch (RuntimeException ex) { throw new IllegalArgumentException( @@ -535,15 +535,15 @@ protected Runnable createRunnable(Object target, Method method) { return new ScheduledMethodRunnable(target, invocableMethod); } - private static long convertToMillis(long value, TimeUnit timeUnit) { - return TimeUnit.MILLISECONDS.convert(value, timeUnit); + private static Duration toDuration(long value, TimeUnit 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)) { - 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) { diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ConcurrentTaskScheduler.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ConcurrentTaskScheduler.java index ca16b976e172..e96b035ba7f4 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ConcurrentTaskScheduler.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ConcurrentTaskScheduler.java @@ -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"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package org.springframework.scheduling.concurrent; import java.time.Clock; +import java.time.Duration; +import java.time.Instant; import java.util.Date; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -32,7 +34,7 @@ import org.springframework.lang.Nullable; import org.springframework.scheduling.TaskScheduler; 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.util.Assert; import org.springframework.util.ClassUtils; @@ -58,6 +60,7 @@ * * @author Juergen Hoeller * @author Mark Fisher + * @author Arjen Poutsma * @since 3.0 * @see java.util.concurrent.ScheduledExecutorService * @see java.util.concurrent.ScheduledThreadPoolExecutor @@ -206,10 +209,10 @@ public ScheduledFuture schedule(Runnable task, Trigger trigger) { } @Override - public ScheduledFuture schedule(Runnable task, Date startTime) { - long initialDelay = startTime.getTime() - this.clock.millis(); + public ScheduledFuture schedule(Runnable task, Instant startTime) { + Duration initialDelay = Duration.between(this.clock.instant(), startTime); 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) { throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex); @@ -217,10 +220,10 @@ public ScheduledFuture schedule(Runnable task, Date startTime) { } @Override - public ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period) { - long initialDelay = startTime.getTime() - this.clock.millis(); + public ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) { + Duration initialDelay = Duration.between(this.clock.instant(), startTime); 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) { throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex); @@ -228,9 +231,9 @@ public ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, lon } @Override - public ScheduledFuture scheduleAtFixedRate(Runnable task, long period) { + public ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period) { 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) { throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex); @@ -238,10 +241,10 @@ public ScheduledFuture scheduleAtFixedRate(Runnable task, long period) { } @Override - public ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay) { - long initialDelay = startTime.getTime() - this.clock.millis(); + public ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) { + Duration initialDelay = Duration.between(this.clock.instant(), startTime); 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) { throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex); @@ -249,9 +252,9 @@ public ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, } @Override - public ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay) { + public ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay) { 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) { throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex); @@ -273,22 +276,66 @@ private Runnable decorateTask(Runnable task, boolean isRepeatingTask) { */ private class EnterpriseConcurrentTriggerScheduler { - public ScheduledFuture schedule(Runnable task, final Trigger trigger) { + public ScheduledFuture schedule(Runnable task, Trigger trigger) { ManagedScheduledExecutorService executor = (ManagedScheduledExecutorService) scheduledExecutor; - return executor.schedule(task, new jakarta.enterprise.concurrent.Trigger() { - @Override + return executor.schedule(task, new TriggerAdapter(trigger)); + } + + 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 - public Date getNextRunTime(@Nullable LastExecution le, Date taskScheduledTime) { - return (trigger.nextExecutionTime(le != null ? - new SimpleTriggerContext(le.getScheduledStart(), le.getRunStart(), le.getRunEnd()) : - new SimpleTriggerContext())); + 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 boolean skipRun(LastExecution lastExecution, Date scheduledRunTime) { - return false; + public Instant lastCompletion() { + return (this.le != null) ? toInstant(this.le.getRunEnd()) : null; } - }); + } } + + } } diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ReschedulingRunnable.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ReschedulingRunnable.java index ca79c80fc6db..bb22d261d02a 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ReschedulingRunnable.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ReschedulingRunnable.java @@ -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"); * you may not use this file except in compliance with the License. @@ -17,7 +17,8 @@ package org.springframework.scheduling.concurrent; 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.ExecutionException; import java.util.concurrent.ScheduledExecutorService; @@ -56,7 +57,7 @@ class ReschedulingRunnable extends DelegatingErrorHandlingRunnable implements Sc private ScheduledFuture currentFuture; @Nullable - private Date scheduledExecutionTime; + private Instant scheduledExecutionTime; private final Object triggerContextMonitor = new Object(); @@ -74,12 +75,12 @@ public ReschedulingRunnable(Runnable delegate, Trigger trigger, Clock clock, @Nullable public ScheduledFuture schedule() { synchronized (this.triggerContextMonitor) { - this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext); + this.scheduledExecutionTime = this.trigger.nextExecution(this.triggerContext); if (this.scheduledExecutionTime == null) { return null; } - long initialDelay = this.scheduledExecutionTime.getTime() - this.triggerContext.getClock().millis(); - this.currentFuture = this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS); + Duration initialDelay = Duration.between(this.triggerContext.getClock().instant(), this.scheduledExecutionTime); + this.currentFuture = this.executor.schedule(this, initialDelay.toMillis(), TimeUnit.MILLISECONDS); return this; } } @@ -91,9 +92,9 @@ private ScheduledFuture obtainCurrentFuture() { @Override public void run() { - Date actualExecutionTime = new Date(this.triggerContext.getClock().millis()); + Instant actualExecutionTime = this.triggerContext.getClock().instant(); super.run(); - Date completionTime = new Date(this.triggerContext.getClock().millis()); + Instant completionTime = this.triggerContext.getClock().instant(); synchronized (this.triggerContextMonitor) { Assert.state(this.scheduledExecutionTime != null, "No scheduled execution"); this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime); diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskScheduler.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskScheduler.java index 2688a853a5fb..fcc25e1e98c1 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskScheduler.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskScheduler.java @@ -17,7 +17,8 @@ package org.springframework.scheduling.concurrent; import java.time.Clock; -import java.util.Date; +import java.time.Duration; +import java.time.Instant; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.Executor; @@ -377,11 +378,11 @@ public ScheduledFuture schedule(Runnable task, Trigger trigger) { } @Override - public ScheduledFuture schedule(Runnable task, Date startTime) { + public ScheduledFuture schedule(Runnable task, Instant startTime) { ScheduledExecutorService executor = getScheduledExecutor(); - long initialDelay = startTime.getTime() - this.clock.millis(); + Duration initialDelay = Duration.between(this.clock.instant(), startTime); try { - return executor.schedule(errorHandlingTask(task, false), initialDelay, TimeUnit.MILLISECONDS); + return executor.schedule(errorHandlingTask(task, false), initialDelay.toMillis(), TimeUnit.MILLISECONDS); } catch (RejectedExecutionException ex) { throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); @@ -389,11 +390,11 @@ public ScheduledFuture schedule(Runnable task, Date startTime) { } @Override - public ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period) { + public ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) { ScheduledExecutorService executor = getScheduledExecutor(); - long initialDelay = startTime.getTime() - this.clock.millis(); + Duration initialDelay = Duration.between(this.clock.instant(), startTime); 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) { throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); @@ -401,10 +402,10 @@ public ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, lon } @Override - public ScheduledFuture scheduleAtFixedRate(Runnable task, long period) { + public ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period) { ScheduledExecutorService executor = getScheduledExecutor(); 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) { throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); @@ -412,11 +413,11 @@ public ScheduledFuture scheduleAtFixedRate(Runnable task, long period) { } @Override - public ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay) { + public ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) { ScheduledExecutorService executor = getScheduledExecutor(); - long initialDelay = startTime.getTime() - this.clock.millis(); + Duration initialDelay = Duration.between(this.clock.instant(), startTime); 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) { throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); @@ -424,10 +425,10 @@ public ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, } @Override - public ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay) { + public ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay) { ScheduledExecutorService executor = getScheduledExecutor(); 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) { throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/FixedDelayTask.java b/spring-context/src/main/java/org/springframework/scheduling/config/FixedDelayTask.java index 2aec5f308930..68f04bd682a6 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/config/FixedDelayTask.java +++ b/spring-context/src/main/java/org/springframework/scheduling/config/FixedDelayTask.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,10 +16,13 @@ package org.springframework.scheduling.config; +import java.time.Duration; + /** * Specialization of {@link IntervalTask} for fixed-delay semantics. * * @author Juergen Hoeller + * @author Arjen Poutsma * @since 5.0.2 * @see org.springframework.scheduling.annotation.Scheduled#fixedDelay() * @see ScheduledTaskRegistrar#addFixedDelayTask(IntervalTask) @@ -31,9 +34,26 @@ public class FixedDelayTask extends IntervalTask { * @param runnable the underlying task to execute * @param interval how often in milliseconds the task should be executed * @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) { 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); + } + } diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/FixedRateTask.java b/spring-context/src/main/java/org/springframework/scheduling/config/FixedRateTask.java index 0b16c574cdd0..73b7cf1707b5 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/config/FixedRateTask.java +++ b/spring-context/src/main/java/org/springframework/scheduling/config/FixedRateTask.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,10 +16,13 @@ package org.springframework.scheduling.config; +import java.time.Duration; + /** * Specialization of {@link IntervalTask} for fixed-rate semantics. * * @author Juergen Hoeller + * @author Arjen Poutsma * @since 5.0.2 * @see org.springframework.scheduling.annotation.Scheduled#fixedRate() * @see ScheduledTaskRegistrar#addFixedRateTask(IntervalTask) @@ -31,9 +34,27 @@ public class FixedRateTask extends IntervalTask { * @param runnable the underlying task to execute * @param interval how often in milliseconds the task should be executed * @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) { 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); + } + + } diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/IntervalTask.java b/spring-context/src/main/java/org/springframework/scheduling/config/IntervalTask.java index b259c7217aa3..dd9aeec54e06 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/config/IntervalTask.java +++ b/spring-context/src/main/java/org/springframework/scheduling/config/IntervalTask.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,21 +16,26 @@ 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 * millisecond interval which may be treated as fixed-rate or fixed-delay depending on * context. * * @author Chris Beams + * @author Arjen Poutsma * @since 3.2 * @see ScheduledTaskRegistrar#addFixedRateTask(IntervalTask) * @see ScheduledTaskRegistrar#addFixedDelayTask(IntervalTask) */ 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 interval how often in milliseconds the task should be executed * @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) { - super(runnable); - this.interval = interval; - this.initialDelay = initialDelay; + this(runnable, Duration.ofMillis(interval), Duration.ofMillis(initialDelay)); } /** * Create a new {@code IntervalTask} with no initial delay. * @param runnable the underlying task to execute * @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) { - 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. + * @deprecated as of 6.0, in favor of {@link #getIntervalDuration()} */ + @Deprecated 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 the initial delay before first execution of the task. + * @deprecated as of 6.0, in favor of {@link #getInitialDelayDuration()} */ + @Deprecated 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; } diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTaskRegistrar.java b/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTaskRegistrar.java index 3b9cc27aa16c..dd692b1374e9 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTaskRegistrar.java +++ b/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTaskRegistrar.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,9 +16,10 @@ package org.springframework.scheduling.config; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; @@ -51,6 +52,7 @@ * @author Chris Beams * @author Tobias Montagna-Hay * @author Sam Brannen + * @author Arjen Poutsma * @since 3.0 * @see org.springframework.scheduling.annotation.EnableAsync * @see org.springframework.scheduling.annotation.SchedulingConfigurer @@ -189,11 +191,11 @@ public List getCronTaskList() { /** * 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 fixedRateTasks) { this.fixedRateTasks = new ArrayList<>(); - fixedRateTasks.forEach(this::addFixedRateTask); + fixedRateTasks.forEach((task, interval) -> addFixedRateTask(task, Duration.ofMillis(interval))); } /** @@ -218,11 +220,11 @@ public List getFixedRateTaskList() { /** * 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 fixedDelayTasks) { this.fixedDelayTasks = new ArrayList<>(); - fixedDelayTasks.forEach(this::addFixedDelayTask); + fixedDelayTasks.forEach((task, delay) -> addFixedDelayTask(task, Duration.ofMillis(delay))); } /** @@ -248,7 +250,7 @@ public List getFixedDelayTaskList() { /** * 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) { addTriggerTask(new TriggerTask(task, trigger)); @@ -257,7 +259,7 @@ public void addTriggerTask(Runnable task, Trigger trigger) { /** * Add a {@code TriggerTask}. * @since 3.2 - * @see TaskScheduler#scheduleAtFixedRate(Runnable, long) + * @see TaskScheduler#scheduleAtFixedRate(Runnable, Duration) */ public void addTriggerTask(TriggerTask task) { if (this.triggerTasks == null) { @@ -290,16 +292,26 @@ public void addCronTask(CronTask task) { /** * 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) { - 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}. * @since 3.2 - * @see TaskScheduler#scheduleAtFixedRate(Runnable, long) + * @see TaskScheduler#scheduleAtFixedRate(Runnable, Duration) */ public void addFixedRateTask(IntervalTask task) { if (this.fixedRateTasks == null) { @@ -310,16 +322,26 @@ public void addFixedRateTask(IntervalTask task) { /** * 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) { - 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}. * @since 3.2 - * @see TaskScheduler#scheduleWithFixedDelay(Runnable, long) + * @see TaskScheduler#scheduleWithFixedDelay(Runnable, Duration) */ public void addFixedDelayTask(IntervalTask task) { if (this.fixedDelayTasks == null) { @@ -353,7 +375,6 @@ public void afterPropertiesSet() { * Schedule all registered tasks against the underlying * {@linkplain #setTaskScheduler(TaskScheduler) task scheduler}. */ - @SuppressWarnings("deprecation") protected void scheduleTasks() { if (this.taskScheduler == null) { this.localExecutor = Executors.newSingleThreadScheduledExecutor(); @@ -371,12 +392,22 @@ protected void scheduleTasks() { } if (this.fixedRateTasks != null) { 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) { 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 ScheduledTask scheduleCronTask(CronTask task) { 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 * or on initialization of the scheduler. @@ -469,14 +484,15 @@ public ScheduledTask scheduleFixedRateTask(FixedRateTask task) { newTask = true; } if (this.taskScheduler != null) { - if (task.getInitialDelay() > 0) { - Date startTime = new Date(this.taskScheduler.getClock().millis() + task.getInitialDelay()); + Duration initialDelay = task.getInitialDelayDuration(); + if (initialDelay.toMillis() > 0) { + Instant startTime = this.taskScheduler.getClock().instant().plus(initialDelay); scheduledTask.future = - this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), startTime, task.getInterval()); + this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), startTime, task.getIntervalDuration()); } else { scheduledTask.future = - this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), task.getInterval()); + this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), task.getIntervalDuration()); } } else { @@ -486,22 +502,6 @@ public ScheduledTask scheduleFixedRateTask(FixedRateTask task) { 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 * or on initialization of the scheduler. @@ -518,14 +518,15 @@ public ScheduledTask scheduleFixedDelayTask(FixedDelayTask task) { newTask = true; } if (this.taskScheduler != null) { - if (task.getInitialDelay() > 0) { - Date startTime = new Date(this.taskScheduler.getClock().millis() + task.getInitialDelay()); + Duration initialDelay = task.getInitialDelayDuration(); + if (!initialDelay.isNegative()) { + Instant startTime = this.taskScheduler.getClock().instant().plus(task.getInitialDelayDuration()); scheduledTask.future = - this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), startTime, task.getInterval()); + this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), startTime, task.getIntervalDuration()); } else { scheduledTask.future = - this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), task.getInterval()); + this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), task.getIntervalDuration()); } } else { diff --git a/spring-context/src/main/java/org/springframework/scheduling/support/CronTrigger.java b/spring-context/src/main/java/org/springframework/scheduling/support/CronTrigger.java index 6581cdd61fe0..74399164a6b0 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/support/CronTrigger.java +++ b/spring-context/src/main/java/org/springframework/scheduling/support/CronTrigger.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,9 +16,9 @@ package org.springframework.scheduling.support; +import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.util.Date; import java.util.TimeZone; import org.springframework.lang.Nullable; @@ -93,23 +93,23 @@ public String getExpression() { * previous execution; therefore, overlapping executions won't occur. */ @Override - public Date nextExecutionTime(TriggerContext triggerContext) { - Date date = triggerContext.lastCompletionTime(); - if (date != null) { - Date scheduled = triggerContext.lastScheduledExecutionTime(); - if (scheduled != null && date.before(scheduled)) { + public Instant nextExecution(TriggerContext triggerContext) { + Instant instant = triggerContext.lastCompletion(); + if (instant != null) { + Instant scheduled = triggerContext.lastScheduledExecution(); + if (scheduled != null && instant.isBefore(scheduled)) { // Previous task apparently executed too early... // Let's simply use the last calculated execution time then, // in order to prevent accidental re-fires in the same second. - date = scheduled; + instant = scheduled; } } 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); - return (next != null ? Date.from(next.toInstant()) : null); + return (next != null ? next.toInstant() : null); } diff --git a/spring-context/src/main/java/org/springframework/scheduling/support/PeriodicTrigger.java b/spring-context/src/main/java/org/springframework/scheduling/support/PeriodicTrigger.java index 9292c0086212..e13ca9489d31 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/support/PeriodicTrigger.java +++ b/spring-context/src/main/java/org/springframework/scheduling/support/PeriodicTrigger.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,7 +16,10 @@ 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 org.springframework.lang.Nullable; @@ -46,18 +49,22 @@ */ 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; /** * 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) { this(period, null); } @@ -66,44 +73,132 @@ public PeriodicTrigger(long period) { * 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 * 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) { - Assert.isTrue(period >= 0, "period must not be negative"); - this.timeUnit = (timeUnit != null ? timeUnit : TimeUnit.MILLISECONDS); - this.period = this.timeUnit.toMillis(period); + this(toDuration(period, timeUnit), timeUnit); + } + + 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. * @since 5.0.2 + * @deprecated as of 6.0, in favor on {@link #getPeriodDuration()} */ + @Deprecated 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 trigger's time unit (milliseconds by default). * @since 5.0.2 + * @deprecated as of 6.0, with no direct replacement */ + @Deprecated 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 * terms of this trigger's {@link TimeUnit}. If no time unit was explicitly * provided upon instantiation, the default is milliseconds. + * @deprecated as of 6.0, in favor of {@link #setInitialDelay(Duration)} */ + @Deprecated 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. * @since 5.0.2 + * @deprecated as of 6.0, in favor on {@link #getInitialDelayDuration()} */ + @Deprecated 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; } @@ -130,16 +225,23 @@ public boolean isFixedRate() { * Returns the time after which a task should run again. */ @Override - public Date nextExecutionTime(TriggerContext triggerContext) { - Date lastExecution = triggerContext.lastScheduledExecutionTime(); - Date lastCompletion = triggerContext.lastCompletionTime(); + public Instant nextExecution(TriggerContext triggerContext) { + Instant lastExecution = triggerContext.lastScheduledExecution(); + Instant lastCompletion = triggerContext.lastCompletion(); 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) { - 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 boolean equals(@Nullable Object other) { if (!(other instanceof PeriodicTrigger otherTrigger)) { return false; } - return (this.fixedRate == otherTrigger.fixedRate && this.initialDelay == otherTrigger.initialDelay && - this.period == otherTrigger.period); + return (this.fixedRate == otherTrigger.fixedRate && + this.period.equals(otherTrigger.period) && + Objects.equals(this.initialDelay, otherTrigger.initialDelay)); } @Override public int hashCode() { - return (this.fixedRate ? 17 : 29) + (int) (37 * this.period) + (int) (41 * this.initialDelay); + return this.period.hashCode(); } } diff --git a/spring-context/src/main/java/org/springframework/scheduling/support/SimpleTriggerContext.java b/spring-context/src/main/java/org/springframework/scheduling/support/SimpleTriggerContext.java index 2c647c34c88f..d6fe019afde5 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/support/SimpleTriggerContext.java +++ b/spring-context/src/main/java/org/springframework/scheduling/support/SimpleTriggerContext.java @@ -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"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.scheduling.support; import java.time.Clock; +import java.time.Instant; import java.util.Date; import org.springframework.lang.Nullable; @@ -33,13 +34,13 @@ public class SimpleTriggerContext implements TriggerContext { private final Clock clock; @Nullable - private volatile Date lastScheduledExecutionTime; + private volatile Instant lastScheduledExecution; @Nullable - private volatile Date lastActualExecutionTime; + private volatile Instant lastActualExecution; @Nullable - private volatile Date lastCompletionTime; + private volatile Instant lastCompletion; /** @@ -56,12 +57,34 @@ public SimpleTriggerContext() { * @param lastScheduledExecutionTime last scheduled execution time * @param lastActualExecutionTime last actual execution 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 scheduled execution time + * @param lastActualExecution last actual execution time + * @param lastCompletion last completion time + */ + public SimpleTriggerContext(@Nullable Instant lastScheduledExecution, @Nullable Instant lastActualExecution, + @Nullable Instant lastCompletion) { + this(); - this.lastScheduledExecutionTime = lastScheduledExecutionTime; - this.lastActualExecutionTime = lastActualExecutionTime; - this.lastCompletionTime = lastCompletionTime; + this.lastScheduledExecution = lastScheduledExecution; + this.lastActualExecution = lastActualExecution; + this.lastCompletion = lastCompletion; } /** @@ -69,7 +92,7 @@ public SimpleTriggerContext(Date lastScheduledExecutionTime, Date lastActualExec * exposing the given clock. * @param clock the clock to use for trigger calculation * @since 5.3 - * @see #update(Date, Date, Date) + * @see #update(Instant, Instant, Instant) */ public SimpleTriggerContext(Clock clock) { this.clock = clock; @@ -81,11 +104,27 @@ public SimpleTriggerContext(Clock clock) { * @param lastScheduledExecutionTime last scheduled execution time * @param lastActualExecutionTime last actual execution 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) { - this.lastScheduledExecutionTime = lastScheduledExecutionTime; - this.lastActualExecutionTime = lastActualExecutionTime; - this.lastCompletionTime = lastCompletionTime; + @Deprecated + public void update(@Nullable Date lastScheduledExecutionTime, @Nullable Date lastActualExecutionTime, + @Nullable Date lastCompletionTime) { + + update(toInstant(lastScheduledExecutionTime), toInstant(lastActualExecutionTime), toInstant(lastCompletionTime)); + } + + /** + * Update this holder's state with the latest time values. + * @param lastScheduledExecution last scheduled execution time + * @param lastActualExecution last actual 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 Clock getClock() { @Override @Nullable - public Date lastScheduledExecutionTime() { - return this.lastScheduledExecutionTime; + public Instant lastScheduledExecution() { + return this.lastScheduledExecution; } @Override @Nullable - public Date lastActualExecutionTime() { - return this.lastActualExecutionTime; + public Instant lastActualExecution() { + return this.lastActualExecution; } @Override @Nullable - public Date lastCompletionTime() { - return this.lastCompletionTime; + public Instant lastCompletion() { + return this.lastCompletion; } } diff --git a/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableSchedulingTests.java b/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableSchedulingTests.java index 28bdafd59f88..a67e98ee503b 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableSchedulingTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableSchedulingTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,8 +16,10 @@ 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.Date; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.AfterEach; @@ -176,7 +178,7 @@ static class FixedRateTaskConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { - taskRegistrar.addFixedRateTask(() -> {}, 100); + taskRegistrar.addFixedRateTask(() -> {}, Duration.ofMillis(100)); } @Bean @@ -423,7 +425,7 @@ public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(taskScheduler()); taskRegistrar.addFixedRateTask(new IntervalTask( () -> worker().executedByThread = Thread.currentThread().getName(), - 10, 0)); + Duration.ofMillis(10))); } } @@ -441,7 +443,7 @@ public TaskScheduler scheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.initialize(); scheduler.schedule(() -> counter().incrementAndGet(), - triggerContext -> new Date(new Date().getTime()+10)); + triggerContext -> Instant.now().plus(10, ChronoUnit.MILLIS)); return scheduler; } } diff --git a/spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java b/spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java index 9aaf52c881f3..81f6ec104907 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -22,13 +22,14 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; -import java.util.Calendar; -import java.util.Date; +import java.time.Duration; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; -import java.util.TimeZone; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterEach; @@ -118,8 +119,8 @@ void fixedDelayTask(@NameToClass Class beanClass, long expectedInterval) { Method targetMethod = runnable.getMethod(); assertThat(targetObject).isEqualTo(target); assertThat(targetMethod.getName()).isEqualTo("fixedDelay"); - assertThat(task.getInitialDelay()).isEqualTo(0L); - assertThat(task.getInterval()).isEqualTo(expectedInterval); + assertThat(task.getInitialDelayDuration()).isZero(); + assertThat(task.getIntervalDuration()).isEqualTo(Duration.ofMillis(expectedInterval)); } @ParameterizedTest @@ -152,8 +153,8 @@ void fixedRateTask(@NameToClass Class beanClass, long expectedInterval) { assertThat(targetObject).isEqualTo(target); assertThat(targetMethod.getName()).isEqualTo("fixedRate"); assertSoftly(softly -> { - softly.assertThat(task.getInitialDelay()).as("initial delay").isEqualTo(0); - softly.assertThat(task.getInterval()).as("interval").isEqualTo(expectedInterval); + softly.assertThat(task.getInitialDelayDuration()).as("initial delay").isZero(); + softly.assertThat(task.getIntervalDuration()).as("interval").isEqualTo(Duration.ofMillis(expectedInterval)); }); } @@ -187,8 +188,8 @@ void fixedRateTaskWithInitialDelay(@NameToClass Class beanClass, long expecte assertThat(targetObject).isEqualTo(target); assertThat(targetMethod.getName()).isEqualTo("fixedRate"); assertSoftly(softly -> { - softly.assertThat(task.getInitialDelay()).as("initial delay").isEqualTo(expectedInitialDelay); - softly.assertThat(task.getInterval()).as("interval").isEqualTo(expectedInterval); + softly.assertThat(task.getInitialDelayDuration()).as("initial delay").isEqualTo(Duration.ofMillis(expectedInitialDelay)); + softly.assertThat(task.getIntervalDuration()).as("interval").isEqualTo(Duration.ofMillis(expectedInterval)); }); } @@ -251,16 +252,16 @@ private void severalFixedRates(StaticApplicationContext context, Method targetMethod = runnable1.getMethod(); assertThat(targetObject).isEqualTo(target); assertThat(targetMethod.getName()).isEqualTo("fixedRate"); - assertThat(task1.getInitialDelay()).isEqualTo(0); - assertThat(task1.getInterval()).isEqualTo(4_000L); + assertThat(task1.getInitialDelayDuration()).isZero(); + assertThat(task1.getIntervalDuration()).isEqualTo(Duration.ofMillis(4_000L)); IntervalTask task2 = fixedRateTasks.get(1); ScheduledMethodRunnable runnable2 = (ScheduledMethodRunnable) task2.getRunnable(); targetObject = runnable2.getTarget(); targetMethod = runnable2.getMethod(); assertThat(targetObject).isEqualTo(target); assertThat(targetMethod.getName()).isEqualTo("fixedRate"); - assertThat(task2.getInitialDelay()).isEqualTo(2_000L); - assertThat(task2.getInterval()).isEqualTo(4_000L); + assertThat(task2.getInitialDelayDuration()).isEqualTo(Duration.ofMillis(2_000L)); + assertThat(task2.getIntervalDuration()).isEqualTo(Duration.ofMillis(4_000L)); } @Test @@ -320,20 +321,20 @@ void cronTaskWithZone() { boolean condition = trigger instanceof CronTrigger; assertThat(condition).isTrue(); CronTrigger cronTrigger = (CronTrigger) trigger; - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+10")); - cal.clear(); - cal.set(2013, 3, 15, 4, 0); // 15-04-2013 4:00 GMT+10 - Date lastScheduledExecutionTime = cal.getTime(); - Date lastActualExecutionTime = cal.getTime(); - cal.add(Calendar.MINUTE, 30); // 4:30 - Date lastCompletionTime = cal.getTime(); + ZonedDateTime dateTime = ZonedDateTime.of(2013, 4, 15, 4, 0, 0, 0, ZoneId.of("GMT+10")); +// Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+10")); +// cal.clear(); +// cal.set(2013, 3, 15, 4, 0); // 15-04-2013 4:00 GMT+10; + Instant lastScheduledExecution = dateTime.toInstant(); + Instant lastActualExecution = dateTime.toInstant(); + dateTime = dateTime.plusMinutes(30); + Instant lastCompletion = dateTime.toInstant(); TriggerContext triggerContext = new SimpleTriggerContext( - lastScheduledExecutionTime, lastActualExecutionTime, lastCompletionTime); - cal.add(Calendar.MINUTE, 30); - cal.add(Calendar.HOUR_OF_DAY, 1); // 6:00 - Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext); + lastScheduledExecution, lastActualExecution, lastCompletion); + dateTime = dateTime.plusMinutes(90); // 6:00 + Instant nextExecutionTime = cronTrigger.nextExecution(triggerContext); // assert that 6:00 is next execution time - assertThat(nextExecutionTime).isEqualTo(cal.getTime()); + assertThat(nextExecutionTime).isEqualTo(dateTime.toInstant()); } @Test @@ -407,7 +408,7 @@ void metaAnnotationWithFixedRate() { Method targetMethod = runnable.getMethod(); assertThat(targetObject).isEqualTo(target); assertThat(targetMethod.getName()).isEqualTo("checkForUpdates"); - assertThat(task.getInterval()).isEqualTo(5_000L); + assertThat(task.getIntervalDuration()).isEqualTo(Duration.ofMillis(5_000L)); } @Test @@ -434,8 +435,8 @@ void composedAnnotationWithInitialDelayAndFixedRate() { Method targetMethod = runnable.getMethod(); assertThat(targetObject).isEqualTo(target); assertThat(targetMethod.getName()).isEqualTo("checkForUpdates"); - assertThat(task.getInterval()).isEqualTo(5_000L); - assertThat(task.getInitialDelay()).isEqualTo(1_000L); + assertThat(task.getIntervalDuration()).isEqualTo(Duration.ofMillis(5_000L)); + assertThat(task.getInitialDelayDuration()).isEqualTo(Duration.ofMillis(1_000L)); } @Test @@ -555,8 +556,8 @@ void propertyPlaceholderWithFixedDelay(@NameToClass Class beanClass, String f assertThat(targetObject).isEqualTo(target); assertThat(targetMethod.getName()).isEqualTo("fixedDelay"); assertSoftly(softly -> { - softly.assertThat(task.getInitialDelay()).as("initial delay").isEqualTo(expectedInitialDelay); - softly.assertThat(task.getInterval()).as("interval").isEqualTo(expectedInterval); + softly.assertThat(task.getInitialDelayDuration()).as("initial delay").isEqualTo(Duration.ofMillis(expectedInitialDelay)); + softly.assertThat(task.getIntervalDuration()).as("interval").isEqualTo(Duration.ofMillis(expectedInterval)); }); } @@ -599,8 +600,8 @@ void propertyPlaceholderWithFixedRate(@NameToClass Class beanClass, String fi assertThat(targetObject).isEqualTo(target); assertThat(targetMethod.getName()).isEqualTo("fixedRate"); assertSoftly(softly -> { - softly.assertThat(task.getInitialDelay()).as("initial delay").isEqualTo(expectedInitialDelay); - softly.assertThat(task.getInterval()).as("interval").isEqualTo(expectedInterval); + softly.assertThat(task.getInitialDelayDuration()).as("initial delay").isEqualTo(Duration.ofMillis(expectedInitialDelay)); + softly.assertThat(task.getIntervalDuration()).as("interval").isEqualTo(Duration.ofMillis(expectedInterval)); }); } diff --git a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolTaskSchedulerTests.java b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolTaskSchedulerTests.java index a96544640628..7109ffea52c7 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolTaskSchedulerTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolTaskSchedulerTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.scheduling.concurrent; +import java.time.Instant; import java.util.Date; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @@ -182,11 +183,11 @@ private static class TestTrigger implements Trigger { } @Override - public Date nextExecutionTime(TriggerContext triggerContext) { + public Instant nextExecution(TriggerContext triggerContext) { if (this.actualRunCount.incrementAndGet() > this.maxRunCount) { return null; } - return new Date(); + return Instant.now(); } } diff --git a/spring-context/src/test/java/org/springframework/scheduling/config/ScheduledTasksBeanDefinitionParserTests.java b/spring-context/src/test/java/org/springframework/scheduling/config/ScheduledTasksBeanDefinitionParserTests.java index 16879f1e7961..dde3a4faa6d4 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/config/ScheduledTasksBeanDefinitionParserTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/config/ScheduledTasksBeanDefinitionParserTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -17,7 +17,8 @@ package org.springframework.scheduling.config; import java.lang.reflect.Method; -import java.util.Date; +import java.time.Duration; +import java.time.Instant; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -78,28 +79,28 @@ public void checkTarget() { public void fixedRateTasks() { List tasks = (List) new DirectFieldAccessor( this.registrar).getPropertyValue("fixedRateTasks"); - assertThat(tasks.size()).isEqualTo(3); - assertThat(tasks.get(0).getInterval()).isEqualTo(1000L); - assertThat(tasks.get(1).getInterval()).isEqualTo(2000L); - assertThat(tasks.get(2).getInterval()).isEqualTo(4000L); - assertThat(tasks.get(2).getInitialDelay()).isEqualTo(500); + assertThat(tasks).hasSize(3); + assertThat(tasks.get(0).getIntervalDuration()).isEqualTo(Duration.ofMillis(1000L)); + assertThat(tasks.get(1).getIntervalDuration()).isEqualTo(Duration.ofMillis(2000L)); + assertThat(tasks.get(2).getIntervalDuration()).isEqualTo(Duration.ofMillis(4000L)); + assertThat(tasks.get(2).getInitialDelayDuration()).isEqualTo(Duration.ofMillis(500)); } @Test public void fixedDelayTasks() { List tasks = (List) new DirectFieldAccessor( this.registrar).getPropertyValue("fixedDelayTasks"); - assertThat(tasks.size()).isEqualTo(2); - assertThat(tasks.get(0).getInterval()).isEqualTo(3000L); - assertThat(tasks.get(1).getInterval()).isEqualTo(3500L); - assertThat(tasks.get(1).getInitialDelay()).isEqualTo(250); + assertThat(tasks).hasSize(2); + assertThat(tasks.get(0).getIntervalDuration()).isEqualTo(Duration.ofMillis(3000L)); + assertThat(tasks.get(1).getIntervalDuration()).isEqualTo(Duration.ofMillis(3500L)); + assertThat(tasks.get(1).getInitialDelayDuration()).isEqualTo(Duration.ofMillis(250)); } @Test public void cronTasks() { List tasks = (List) new DirectFieldAccessor( this.registrar).getPropertyValue("cronTasks"); - assertThat(tasks.size()).isEqualTo(1); + assertThat(tasks).hasSize(1); assertThat(tasks.get(0).getExpression()).isEqualTo("*/4 * 9-17 * * MON-FRI"); } @@ -107,7 +108,7 @@ public void cronTasks() { public void triggerTasks() { List tasks = (List) new DirectFieldAccessor( this.registrar).getPropertyValue("triggerTasks"); - assertThat(tasks.size()).isEqualTo(1); + assertThat(tasks).hasSize(1); assertThat(tasks.get(0).getTrigger()).isInstanceOf(TestTrigger.class); } @@ -122,7 +123,7 @@ public void test() { static class TestTrigger implements Trigger { @Override - public Date nextExecutionTime(TriggerContext triggerContext) { + public Instant nextExecution(TriggerContext triggerContext) { return null; } } diff --git a/spring-context/src/test/java/org/springframework/scheduling/support/PeriodicTriggerTests.java b/spring-context/src/test/java/org/springframework/scheduling/support/PeriodicTriggerTests.java index 60e09f07962b..ba466774a60f 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/support/PeriodicTriggerTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/support/PeriodicTriggerTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,11 +16,13 @@ package org.springframework.scheduling.support; -import java.util.Date; -import java.util.concurrent.TimeUnit; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import org.junit.jupiter.api.Test; +import org.springframework.lang.Nullable; import org.springframework.scheduling.TriggerContext; import org.springframework.util.NumberUtils; @@ -34,149 +36,149 @@ public class PeriodicTriggerTests { @Test public void fixedDelayFirstExecution() { - Date now = new Date(); - PeriodicTrigger trigger = new PeriodicTrigger(5000); - Date next = trigger.nextExecutionTime(context(null, null, null)); + Instant now = Instant.now(); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(5000)); + Instant next = trigger.nextExecution(context(null, null, null)); assertNegligibleDifference(now, next); } @Test public void fixedDelayWithInitialDelayFirstExecution() { - Date now = new Date(); + Instant now = Instant.now(); long period = 5000; long initialDelay = 30000; - PeriodicTrigger trigger = new PeriodicTrigger(period); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(period)); trigger.setInitialDelay(initialDelay); - Date next = trigger.nextExecutionTime(context(null, null, null)); + Instant next = trigger.nextExecution(context(null, null, null)); assertApproximateDifference(now, next, initialDelay); } @Test public void fixedDelayWithTimeUnitFirstExecution() { - Date now = new Date(); - PeriodicTrigger trigger = new PeriodicTrigger(5, TimeUnit.SECONDS); - Date next = trigger.nextExecutionTime(context(null, null, null)); + Instant now = Instant.now(); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofSeconds(5)); + Instant next = trigger.nextExecution(context(null, null, null)); assertNegligibleDifference(now, next); } @Test public void fixedDelayWithTimeUnitAndInitialDelayFirstExecution() { - Date now = new Date(); + Instant now = Instant.now(); long period = 5; long initialDelay = 30; - PeriodicTrigger trigger = new PeriodicTrigger(period, TimeUnit.SECONDS); - trigger.setInitialDelay(initialDelay); - Date next = trigger.nextExecutionTime(context(null, null, null)); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofSeconds(period)); + trigger.setInitialDelay(Duration.ofSeconds(initialDelay)); + Instant next = trigger.nextExecution(context(null, null, null)); assertApproximateDifference(now, next, initialDelay * 1000); } @Test public void fixedDelaySubsequentExecution() { - Date now = new Date(); + Instant now = Instant.now(); long period = 5000; - PeriodicTrigger trigger = new PeriodicTrigger(period); - Date next = trigger.nextExecutionTime(context(now, 500, 3000)); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(period)); + Instant next = trigger.nextExecution(context(now, 500, 3000)); assertApproximateDifference(now, next, period + 3000); } @Test public void fixedDelayWithInitialDelaySubsequentExecution() { - Date now = new Date(); + Instant now = Instant.now(); long period = 5000; long initialDelay = 30000; - PeriodicTrigger trigger = new PeriodicTrigger(period); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(period)); trigger.setInitialDelay(initialDelay); - Date next = trigger.nextExecutionTime(context(now, 500, 3000)); + Instant next = trigger.nextExecution(context(now, 500, 3000)); assertApproximateDifference(now, next, period + 3000); } @Test public void fixedDelayWithTimeUnitSubsequentExecution() { - Date now = new Date(); + Instant now = Instant.now(); long period = 5; - PeriodicTrigger trigger = new PeriodicTrigger(period, TimeUnit.SECONDS); - Date next = trigger.nextExecutionTime(context(now, 500, 3000)); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofSeconds(period)); + Instant next = trigger.nextExecution(context(now, 500, 3000)); assertApproximateDifference(now, next, (period * 1000) + 3000); } @Test public void fixedRateFirstExecution() { - Date now = new Date(); - PeriodicTrigger trigger = new PeriodicTrigger(5000); + Instant now = Instant.now(); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(5000)); trigger.setFixedRate(true); - Date next = trigger.nextExecutionTime(context(null, null, null)); + Instant next = trigger.nextExecution(context(null, null, null)); assertNegligibleDifference(now, next); } @Test public void fixedRateWithTimeUnitFirstExecution() { - Date now = new Date(); - PeriodicTrigger trigger = new PeriodicTrigger(5, TimeUnit.SECONDS); + Instant now = Instant.now(); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofSeconds(5)); trigger.setFixedRate(true); - Date next = trigger.nextExecutionTime(context(null, null, null)); + Instant next = trigger.nextExecution(context(null, null, null)); assertNegligibleDifference(now, next); } @Test public void fixedRateWithInitialDelayFirstExecution() { - Date now = new Date(); + Instant now = Instant.now(); long period = 5000; long initialDelay = 30000; - PeriodicTrigger trigger = new PeriodicTrigger(period); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(period)); trigger.setFixedRate(true); trigger.setInitialDelay(initialDelay); - Date next = trigger.nextExecutionTime(context(null, null, null)); + Instant next = trigger.nextExecution(context(null, null, null)); assertApproximateDifference(now, next, initialDelay); } @Test public void fixedRateWithTimeUnitAndInitialDelayFirstExecution() { - Date now = new Date(); + Instant now = Instant.now(); long period = 5; long initialDelay = 30; - PeriodicTrigger trigger = new PeriodicTrigger(period, TimeUnit.MINUTES); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMinutes(period)); trigger.setFixedRate(true); - trigger.setInitialDelay(initialDelay); - Date next = trigger.nextExecutionTime(context(null, null, null)); + trigger.setInitialDelay(Duration.ofMinutes(initialDelay)); + Instant next = trigger.nextExecution(context(null, null, null)); assertApproximateDifference(now, next, (initialDelay * 60 * 1000)); } @Test public void fixedRateSubsequentExecution() { - Date now = new Date(); + Instant now = Instant.now(); long period = 5000; - PeriodicTrigger trigger = new PeriodicTrigger(period); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(period)); trigger.setFixedRate(true); - Date next = trigger.nextExecutionTime(context(now, 500, 3000)); + Instant next = trigger.nextExecution(context(now, 500, 3000)); assertApproximateDifference(now, next, period); } @Test public void fixedRateWithInitialDelaySubsequentExecution() { - Date now = new Date(); + Instant now = Instant.now(); long period = 5000; long initialDelay = 30000; - PeriodicTrigger trigger = new PeriodicTrigger(period); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofMillis(period)); trigger.setFixedRate(true); trigger.setInitialDelay(initialDelay); - Date next = trigger.nextExecutionTime(context(now, 500, 3000)); + Instant next = trigger.nextExecution(context(now, 500, 3000)); assertApproximateDifference(now, next, period); } @Test public void fixedRateWithTimeUnitSubsequentExecution() { - Date now = new Date(); + Instant now = Instant.now(); long period = 5; - PeriodicTrigger trigger = new PeriodicTrigger(period, TimeUnit.HOURS); + PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofHours(period)); 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)); } @Test public void equalsVerification() { - PeriodicTrigger trigger1 = new PeriodicTrigger(3000); - PeriodicTrigger trigger2 = new PeriodicTrigger(3000); + PeriodicTrigger trigger1 = new PeriodicTrigger(Duration.ofMillis(3000)); + PeriodicTrigger trigger2 = new PeriodicTrigger(Duration.ofMillis(3000)); assertThat(trigger1.equals(new String("not a trigger"))).isFalse(); assertThat(trigger1.equals(null)).isFalse(); assertThat(trigger1).isEqualTo(trigger1); @@ -192,44 +194,46 @@ public void equalsVerification() { assertThat(trigger2.equals(trigger1)).isFalse(); trigger1.setFixedRate(true); assertThat(trigger2).isEqualTo(trigger1); - PeriodicTrigger trigger3 = new PeriodicTrigger(3, TimeUnit.SECONDS); - trigger3.setInitialDelay(7); + PeriodicTrigger trigger3 = new PeriodicTrigger(Duration.ofSeconds(3)); + trigger3.setInitialDelay(Duration.ofSeconds(7)); trigger3.setFixedRate(true); assertThat(trigger1.equals(trigger3)).isFalse(); assertThat(trigger3.equals(trigger1)).isFalse(); - trigger1.setInitialDelay(7000); + trigger1.setInitialDelay(Duration.ofMillis(7000)); assertThat(trigger3).isEqualTo(trigger1); } // utility methods - private static void assertNegligibleDifference(Date d1, Date d2) { - long diff = Math.abs(d1.getTime() - d2.getTime()); - assertThat(diff < 100).as("difference exceeds threshold: " + diff).isTrue(); + private static void assertNegligibleDifference(Instant d1, @Nullable Instant d2) { + assertThat(Duration.between(d1, d2)).isLessThan(Duration.ofMillis(100)); } - private static void assertApproximateDifference(Date lesser, Date greater, long expected) { - long diff = greater.getTime() - lesser.getTime(); + private static void assertApproximateDifference(Instant lesser, Instant greater, long expected) { + long diff = greater.toEpochMilli() - lesser.toEpochMilli(); long variance = Math.abs(expected - diff); assertThat(variance < 100).as("expected approximate difference of " + expected + ", but actual difference was " + diff).isTrue(); } - private static TriggerContext context(Object scheduled, Object actual, Object completion) { - return new TestTriggerContext(asDate(scheduled), asDate(actual), asDate(completion)); + private static TriggerContext context(@Nullable Object scheduled, @Nullable Object actual, + @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) { return null; } - if (o instanceof Date) { - return (Date) o; + if (o instanceof Instant) { + return (Instant) o; } if (o instanceof Number) { - return new Date(System.currentTimeMillis() + - NumberUtils.convertNumberToTargetClass((Number) o, Long.class)); + return Instant.now() + .plus(NumberUtils.convertNumberToTargetClass((Number) o, Long.class), + ChronoUnit.MILLIS); } throw new IllegalArgumentException( "expected Date or Number, but actual type was: " + o.getClass()); @@ -240,30 +244,35 @@ private static Date asDate(Object o) { private static class TestTriggerContext implements TriggerContext { - private final Date scheduled; + @Nullable + private final Instant scheduled; + + @Nullable + private final Instant actual; - private final Date actual; + @Nullable + private final Instant completion; - private final Date completion; + TestTriggerContext(@Nullable Instant scheduled, + @Nullable Instant actual, @Nullable Instant completion) { - TestTriggerContext(Date scheduled, Date actual, Date completion) { this.scheduled = scheduled; this.actual = actual; this.completion = completion; } @Override - public Date lastActualExecutionTime() { + public Instant lastActualExecution() { return this.actual; } @Override - public Date lastCompletionTime() { + public Instant lastCompletion() { return this.completion; } @Override - public Date lastScheduledExecutionTime() { + public Instant lastScheduledExecution() { return this.scheduled; } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java index 7f795eb67e30..c954bc71bace 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java @@ -17,6 +17,7 @@ package org.springframework.messaging.simp.broker; import java.security.Principal; +import java.time.Duration; import java.util.Arrays; import java.util.Collection; import java.util.Map; @@ -254,8 +255,8 @@ public MessageHeaderInitializer getHeaderInitializer() { public void startInternal() { publishBrokerAvailableEvent(); if (this.taskScheduler != null) { - long interval = initHeartbeatTaskDelay(); - if (interval > 0) { + Duration interval = initHeartbeatTaskDelay(); + if (interval.toMillis() > 0) { this.heartbeatFuture = this.taskScheduler.scheduleWithFixedDelay(new HeartbeatTask(), interval); } } @@ -266,15 +267,15 @@ public void startInternal() { } } - private long initHeartbeatTaskDelay() { + private Duration initHeartbeatTaskDelay() { if (getHeartbeatValue() == null) { - return 0; + return Duration.ZERO; } 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 { - return (getHeartbeatValue()[0] > 0 ? getHeartbeatValue()[0] : getHeartbeatValue()[1]); + return Duration.ofMillis(getHeartbeatValue()[0] > 0 ? getHeartbeatValue()[0] : getHeartbeatValue()[1]); } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/DefaultStompSession.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/DefaultStompSession.java index 391325f443d7..576ba7c1a4bf 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/DefaultStompSession.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/DefaultStompSession.java @@ -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"); * you may not use this file except in compliance with the License. @@ -17,9 +17,9 @@ package org.springframework.messaging.simp.stomp; import java.lang.reflect.Type; +import java.time.Instant; import java.util.ArrayList; import java.util.Collections; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -566,7 +566,7 @@ public ReceiptHandler(@Nullable String receiptId) { private void initReceiptHandling() { Assert.notNull(getTaskScheduler(), "To track receipts, a TaskScheduler must be configured"); 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); } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java index 4a6b1257014b..ec7d60c4428b 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java @@ -17,6 +17,7 @@ package org.springframework.messaging.simp.stomp; import java.security.Principal; +import java.time.Duration; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -457,7 +458,7 @@ protected void startInternal() { this.tcpClient.connect(handler, new FixedIntervalReconnectStrategy(5000)); if (this.taskScheduler != null) { - this.taskScheduler.scheduleWithFixedDelay(new ClientSendMessageCountTask(), 5000); + this.taskScheduler.scheduleWithFixedDelay(new ClientSendMessageCountTask(), Duration.ofMillis(5000)); } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/user/UserRegistryMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/user/UserRegistryMessageHandler.java index 61d35a174a03..eb55f0110e40 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/user/UserRegistryMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/user/UserRegistryMessageHandler.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.messaging.simp.user; +import java.time.Duration; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -111,7 +112,7 @@ public long getRegistryExpirationPeriod() { @Override public void onApplicationEvent(BrokerAvailabilityEvent event) { if (event.isBrokerAvailable()) { - long delay = getRegistryExpirationPeriod() / 2; + Duration delay = Duration.ofMillis(getRegistryExpirationPeriod() / 2); this.scheduledFuture = this.scheduler.scheduleWithFixedDelay(this.schedulerTask, delay); } else { diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandlerTests.java index 72807d3e600e..eba17ef3e7fd 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandlerTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandlerTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.messaging.simp.broker; import java.security.Principal; +import java.time.Duration; import java.util.Collections; import java.util.List; import java.util.concurrent.ScheduledFuture; @@ -184,13 +185,13 @@ public void startWithHeartbeatValueWithoutTaskScheduler() { @SuppressWarnings("rawtypes") public void startAndStopWithHeartbeatValue() { 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.setHeartbeatValue(new long[] {15000, 16000}); 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); this.messageHandler.stop(); @@ -205,7 +206,7 @@ public void startWithOneZeroHeartbeatValue() { this.messageHandler.setHeartbeatValue(new long[] {0, 10000}); this.messageHandler.start(); - verify(this.taskScheduler).scheduleWithFixedDelay(any(Runnable.class), eq(10000L)); + verify(this.taskScheduler).scheduleWithFixedDelay(any(Runnable.class), eq(Duration.ofMillis(10000))); } @Test @@ -215,7 +216,7 @@ public void readInactivity() throws Exception { this.messageHandler.start(); ArgumentCaptor 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(); assertThat(heartbeatTask).isNotNull(); @@ -246,7 +247,7 @@ public void writeInactivity() throws Exception { this.messageHandler.start(); ArgumentCaptor 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(); assertThat(heartbeatTask).isNotNull(); @@ -277,7 +278,7 @@ public void readWriteIntervalCalculation() throws Exception { this.messageHandler.start(); ArgumentCaptor 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(); assertThat(heartbeatTask).isNotNull(); diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/DefaultStompSessionTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/DefaultStompSessionTests.java index 7f979b54213e..b1c180dc7e6c 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/DefaultStompSessionTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/DefaultStompSessionTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -17,8 +17,8 @@ package org.springframework.messaging.simp.stomp; import java.nio.charset.StandardCharsets; +import java.time.Instant; import java.util.Arrays; -import java.util.Date; import java.util.Map; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicReference; @@ -628,7 +628,7 @@ public void receiptNotReceived() { AtomicReference notReceived = new AtomicReference<>(); 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(); headers.setDestination("/topic/foo"); @@ -637,7 +637,7 @@ public void receiptNotReceived() { receiptable.addReceiptLostTask(() -> notReceived.set(true)); ArgumentCaptor taskCaptor = ArgumentCaptor.forClass(Runnable.class); - verify(taskScheduler).schedule(taskCaptor.capture(), (Date) notNull()); + verify(taskScheduler).schedule(taskCaptor.capture(), (Instant) notNull()); Runnable scheduledTask = taskCaptor.getValue(); assertThat(scheduledTask).isNotNull(); diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandlerTests.java index 44f52a68b4e5..05e9efe81d6d 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandlerTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandlerTests.java @@ -16,6 +16,7 @@ package org.springframework.messaging.simp.stomp; +import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -166,7 +167,7 @@ void destinationExcludedWithHeartbeat() { this.tcpClient.handleMessage(MessageBuilder.createMessage(new byte[0], accessor.getMessageHeaders())); // 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(); accessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE); diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/user/UserRegistryMessageHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/user/UserRegistryMessageHandlerTests.java index e01348ff64d4..60e06786a9ae 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/user/UserRegistryMessageHandlerTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/user/UserRegistryMessageHandlerTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.messaging.simp.user; +import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -94,7 +95,7 @@ public void brokerAvailableEvent() throws Exception { public void brokerUnavailableEvent() throws Exception { 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); this.handler.onApplicationEvent(event); @@ -181,7 +182,7 @@ private Runnable getUserRegistryTask() { this.handler.onApplicationEvent(event); ArgumentCaptor 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(); } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketStompClient.java b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketStompClient.java index 7c3c7fd045e4..1b65927d016f 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketStompClient.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketStompClient.java @@ -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"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; +import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -403,6 +404,7 @@ private void updateLastWriteTime() { public void onReadInactivity(final Runnable runnable, final long duration) { Assert.state(getTaskScheduler() != null, "No TaskScheduler configured"); this.lastReadTime = System.currentTimeMillis(); + Duration delay = Duration.ofMillis(duration / 2); this.inactivityTasks.add(getTaskScheduler().scheduleWithFixedDelay(() -> { if (System.currentTimeMillis() - this.lastReadTime > duration) { try { @@ -414,13 +416,14 @@ public void onReadInactivity(final Runnable runnable, final long duration) { } } } - }, duration / 2)); + }, delay)); } @Override public void onWriteInactivity(final Runnable runnable, final long duration) { Assert.state(getTaskScheduler() != null, "No TaskScheduler configured"); this.lastWriteTime = System.currentTimeMillis(); + Duration delay = Duration.ofMillis(duration / 2); this.inactivityTasks.add(getTaskScheduler().scheduleWithFixedDelay(() -> { if (System.currentTimeMillis() - this.lastWriteTime > duration) { try { @@ -432,7 +435,7 @@ public void onWriteInactivity(final Runnable runnable, final long duration) { } } } - }, duration / 2)); + }, delay)); } @Override diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/DefaultTransportRequest.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/DefaultTransportRequest.java index e69f0e2ce60b..fc087e991b98 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/DefaultTransportRequest.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/DefaultTransportRequest.java @@ -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"); * you may not use this file except in compliance with the License. @@ -18,8 +18,9 @@ import java.net.URI; import java.security.Principal; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -160,7 +161,7 @@ private void scheduleConnectTimeoutTask(ConnectCallback connectHandler) { if (logger.isTraceEnabled()) { 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); } else if (logger.isTraceEnabled()) { diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java index d456a72a5dd3..1f64138c526c 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java @@ -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"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import java.io.IOException; import java.security.Principal; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -380,6 +381,7 @@ private void scheduleSessionTask() { if (this.sessionCleanupTask != null) { return; } + Duration disconnectDelay = Duration.ofMillis(getDisconnectDelay()); this.sessionCleanupTask = getTaskScheduler().scheduleAtFixedRate(() -> { List removedIds = new ArrayList<>(); for (SockJsSession session : this.sessions.values()) { @@ -398,7 +400,7 @@ private void scheduleSessionTask() { if (logger.isDebugEnabled() && !removedIds.isEmpty()) { logger.debug("Closed " + removedIds.size() + " sessions: " + removedIds); } - }, getDisconnectDelay()); + }, disconnectDelay); } } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java index ad334fe9e107..2ea6c1a4fb4d 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java @@ -17,9 +17,10 @@ package org.springframework.web.socket.sockjs.transport.session; import java.io.IOException; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Collections; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; @@ -270,7 +271,7 @@ protected void scheduleHeartbeat() { if (!isActive()) { 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.heartbeatFuture = this.config.getTaskScheduler().schedule(this.heartbeatTask, time); if (logger.isTraceEnabled()) { diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/config/HandlersBeanDefinitionParserTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/config/HandlersBeanDefinitionParserTests.java index 6bf407c83a6e..d146858d978d 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/config/HandlersBeanDefinitionParserTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/config/HandlersBeanDefinitionParserTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -18,7 +18,8 @@ import java.io.IOException; import java.io.InputStream; -import java.util.Date; +import java.time.Duration; +import java.time.Instant; import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledFuture; @@ -320,27 +321,27 @@ public ScheduledFuture schedule(Runnable task, Trigger trigger) { } @Override - public ScheduledFuture schedule(Runnable task, Date startTime) { + public ScheduledFuture schedule(Runnable task, Instant startTime) { return null; } @Override - public ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period) { + public ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) { return null; } @Override - public ScheduledFuture scheduleAtFixedRate(Runnable task, long period) { + public ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period) { return null; } @Override - public ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay) { + public ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) { return null; } @Override - public ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay) { + public ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay) { return null; } } diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/messaging/WebSocketStompClientTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/messaging/WebSocketStompClientTests.java index 4d849bde949c..fba4cf89a7be 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/messaging/WebSocketStompClientTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/messaging/WebSocketStompClientTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import java.net.URI; import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.concurrent.ScheduledFuture; import org.junit.jupiter.api.BeforeEach; @@ -295,7 +296,7 @@ public void cancelInactivityTasks() throws Exception { TcpConnection tcpConnection = getTcpConnection(); 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.onWriteInactivity(mock(Runnable.class), 2L); @@ -332,7 +333,7 @@ private void testInactivityTaskScheduling(Runnable runnable, long delay, long sl throws InterruptedException { ArgumentCaptor 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); if (sleepTime > 0) { diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/DefaultTransportRequestTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/DefaultTransportRequestTests.java index b0ce13365383..25bf90e2b7c1 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/DefaultTransportRequestTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/DefaultTransportRequestTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import java.io.IOException; import java.net.URI; -import java.util.Date; +import java.time.Instant; import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.BeforeEach; @@ -115,7 +115,7 @@ public void fallbackAfterTimeout() throws Exception { // Get and invoke the scheduled timeout task ArgumentCaptor taskCaptor = ArgumentCaptor.forClass(Runnable.class); - verify(scheduler).schedule(taskCaptor.capture(), any(Date.class)); + verify(scheduler).schedule(taskCaptor.capture(), any(Instant.class)); verifyNoMoreInteractions(scheduler); taskCaptor.getValue().run(); diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/DefaultSockJsServiceTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/DefaultSockJsServiceTests.java index 3a651c015b4b..fbd16e581a37 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/DefaultSockJsServiceTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/DefaultSockJsServiceTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.web.socket.sockjs.transport.handler; +import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -153,7 +154,7 @@ public void handleTransportRequestXhr() throws Exception { assertThat(this.servletResponse.getStatus()).isEqualTo(200); 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.servletResponse.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isNull(); diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/HttpSendingTransportHandlerTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/HttpSendingTransportHandlerTests.java index 48cb9bd4392e..4eafd96a4cc5 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/HttpSendingTransportHandlerTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/HttpSendingTransportHandlerTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ 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.Test; @@ -81,7 +81,7 @@ public void handleRequestXhr() throws Exception { transportHandler.handleRequest(this.request, this.response, this.webSocketHandler, session); 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(); transportHandler.handleRequest(this.request, this.response, this.webSocketHandler, session); diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java index 2421ded37e43..649568878df6 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -17,8 +17,8 @@ package org.springframework.web.socket.sockjs.transport.session; import java.io.IOException; +import java.time.Instant; import java.util.Collections; -import java.util.Date; import java.util.concurrent.ScheduledFuture; import org.junit.jupiter.api.Test; @@ -262,7 +262,7 @@ public void sendHeartbeat() { assertThat(this.session.getSockJsFramesWritten().size()).isEqualTo(1); 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); } @@ -286,12 +286,12 @@ public void sendHeartbeatWhenDisabled() { @Test public void scheduleAndCancelHeartbeat() { 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.scheduleHeartbeat(); - verify(this.taskScheduler).schedule(any(Runnable.class), any(Date.class)); + verify(this.taskScheduler).schedule(any(Runnable.class), any(Instant.class)); verifyNoMoreInteractions(this.taskScheduler); given(task.isCancelled()).willReturn(false); diff --git a/src/docs/asciidoc/integration.adoc b/src/docs/asciidoc/integration.adoc index 947a734f94c1..b8f0da9eb1f4 100644 --- a/src/docs/asciidoc/integration.adoc +++ b/src/docs/asciidoc/integration.adoc @@ -3836,27 +3836,17 @@ The following listing shows the `TaskScheduler` interface definition: ScheduledFuture schedule(Runnable task, Instant startTime); - ScheduledFuture schedule(Runnable task, Date startTime); - 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, long period); - 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, 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 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