diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java index 045dcd8a95..c2aa1df37b 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/Scheduled.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 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. @@ -86,6 +86,7 @@ public @interface Scheduled { * Execute the annotated method with a fixed period in milliseconds between the * end of the last invocation and the start of the next. * @return the delay in milliseconds as a String value, e.g. a placeholder + * or a {@link java.time.Duration#parse java.time.Duration} compliant value * @since 3.2.2 */ String fixedDelayString() default ""; @@ -101,6 +102,7 @@ public @interface Scheduled { * Execute the annotated method with a fixed period in milliseconds between * invocations. * @return the period in milliseconds as a String value, e.g. a placeholder + * or a {@link java.time.Duration#parse java.time.Duration} compliant value * @since 3.2.2 */ String fixedRateString() default ""; @@ -117,6 +119,7 @@ public @interface Scheduled { * Number of milliseconds to delay before the first execution of a * {@link #fixedRate()} or {@link #fixedDelay()} task. * @return the initial delay in milliseconds as a String value, e.g. a placeholder + * or a {@link java.time.Duration#parse java.time.Duration} compliant value * @since 3.2.2 */ String initialDelayString() default ""; 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 c8a0ad1357..56f7c2d7a1 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 @@ -17,6 +17,7 @@ package org.springframework.scheduling.annotation; import java.lang.reflect.Method; +import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -362,9 +363,9 @@ public class ScheduledAnnotationBeanPostProcessor } if (StringUtils.hasLength(initialDelayString)) { try { - initialDelay = Long.parseLong(initialDelayString); + initialDelay = parseDelayAsLong(initialDelayString); } - catch (NumberFormatException ex) { + catch (RuntimeException ex) { throw new IllegalArgumentException( "Invalid initialDelayString value \"" + initialDelayString + "\" - cannot parse into long"); } @@ -414,9 +415,9 @@ public class ScheduledAnnotationBeanPostProcessor Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; try { - fixedDelay = Long.parseLong(fixedDelayString); + fixedDelay = parseDelayAsLong(fixedDelayString); } - catch (NumberFormatException ex) { + catch (RuntimeException ex) { throw new IllegalArgumentException( "Invalid fixedDelayString value \"" + fixedDelayString + "\" - cannot parse into long"); } @@ -440,9 +441,9 @@ public class ScheduledAnnotationBeanPostProcessor Assert.isTrue(!processedSchedule, errorMessage); processedSchedule = true; try { - fixedRate = Long.parseLong(fixedRateString); + fixedRate = parseDelayAsLong(fixedRateString); } - catch (NumberFormatException ex) { + catch (RuntimeException ex) { throw new IllegalArgumentException( "Invalid fixedRateString value \"" + fixedRateString + "\" - cannot parse into long"); } @@ -469,6 +470,17 @@ public class ScheduledAnnotationBeanPostProcessor } } + private static long parseDelayAsLong(String value) throws RuntimeException { + if (value.length() > 1 && (isP(value.charAt(0)) || isP(value.charAt(1)))) { + return Duration.parse(value).toMillis(); + } + return Long.parseLong(value); + } + + private static boolean isP(char ch) { + return (ch == 'P' || ch == 'p'); + } + /** * Return all currently scheduled tasks, from {@link Scheduled} methods 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 9b262718b6..75693b23ed 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 @@ -425,12 +425,21 @@ public class ScheduledAnnotationBeanPostProcessorTests { } @Test - public void propertyPlaceholderWithFixedDelay() { + public void propertyPlaceholderWithFixedDelayInMillis() { + propertyPlaceholderWithFixedDelay(false); + } + + @Test + public void propertyPlaceholderWithFixedDelayInDuration() { + propertyPlaceholderWithFixedDelay(true); + } + + private void propertyPlaceholderWithFixedDelay(boolean durationFormat) { BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class); BeanDefinition placeholderDefinition = new RootBeanDefinition(PropertyPlaceholderConfigurer.class); Properties properties = new Properties(); - properties.setProperty("fixedDelay", "5000"); - properties.setProperty("initialDelay", "1000"); + properties.setProperty("fixedDelay", (durationFormat ? "PT5S" : "5000")); + properties.setProperty("initialDelay", (durationFormat ? "PT1S" : "1000")); placeholderDefinition.getPropertyValues().addPropertyValue("properties", properties); BeanDefinition targetDefinition = new RootBeanDefinition(PropertyPlaceholderWithFixedDelayTestBean.class); context.registerBeanDefinition("postProcessor", processorDefinition); @@ -457,12 +466,21 @@ public class ScheduledAnnotationBeanPostProcessorTests { } @Test - public void propertyPlaceholderWithFixedRate() { + public void propertyPlaceholderWithFixedRateInMillis() { + propertyPlaceholderWithFixedRate(false); + } + + @Test + public void propertyPlaceholderWithFixedRateInDuration() { + propertyPlaceholderWithFixedRate(true); + } + + private void propertyPlaceholderWithFixedRate(boolean durationFormat) { BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class); BeanDefinition placeholderDefinition = new RootBeanDefinition(PropertyPlaceholderConfigurer.class); Properties properties = new Properties(); - properties.setProperty("fixedRate", "3000"); - properties.setProperty("initialDelay", "1000"); + properties.setProperty("fixedRate", (durationFormat ? "PT3S" : "3000")); + properties.setProperty("initialDelay", (durationFormat ? "PT1S" : "1000")); placeholderDefinition.getPropertyValues().addPropertyValue("properties", properties); BeanDefinition targetDefinition = new RootBeanDefinition(PropertyPlaceholderWithFixedRateTestBean.class); context.registerBeanDefinition("postProcessor", processorDefinition);