From cbaf0fa68608cbd9efa334edcb4642f96100a875 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sun, 19 Nov 2017 21:03:22 -0800 Subject: [PATCH] Support Duration in generated meta-data Update the configuration properties annotation processor to deal with `Duration` based default values. For example a field that defaults to `Duration.ofSeconds(10)` will have a meta-data default value of `10s`. See gh-11080 --- ...figurationMetadataAnnotationProcessor.java | 5 ++- .../javac/JavaCompilerFieldValuesParser.java | 40 +++++++++++++++---- ...ationMetadataAnnotationProcessorTests.java | 15 +++---- .../AbstractFieldValuesProcessorTests.java | 7 ++++ .../fieldvalues/FieldValues.java | 15 +++++++ 5 files changed, 66 insertions(+), 16 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java index 80a67e26e2..e0e7b20f3b 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java @@ -19,6 +19,7 @@ package org.springframework.boot.configurationprocessor; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.io.StringWriter; +import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -424,8 +425,8 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor String.format("Enable the %s endpoint.", endpointId), (enabledByDefault == null ? true : enabledByDefault), null)); this.metadataCollector.add(ItemMetadata.newProperty(endpointKey(endpointId), - "cache.time-to-live", Long.class.getName(), type, null, - "Maximum time in milliseconds that a response can be cached.", 0, null)); + "cache.time-to-live", Duration.class.getName(), type, null, + "Maximum time that a response can be cached.", 0, null)); } private String endpointKey(String suffix) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/javac/JavaCompilerFieldValuesParser.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/javac/JavaCompilerFieldValuesParser.java index dbf0809858..e66f66022a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/javac/JavaCompilerFieldValuesParser.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/javac/JavaCompilerFieldValuesParser.java @@ -76,7 +76,7 @@ public class JavaCompilerFieldValuesParser implements FieldValuesParser { WRAPPER_TYPES = Collections.unmodifiableMap(types); } - private static final Map, Object> defaultTypeValues; + private static final Map, Object> DEFAULT_TYPE_VALUES; static { Map, Object> values = new HashMap<>(); @@ -85,10 +85,10 @@ public class JavaCompilerFieldValuesParser implements FieldValuesParser { values.put(Short.class, (short) 0); values.put(Integer.class, 0); values.put(Long.class, (long) 0); - defaultTypeValues = Collections.unmodifiableMap(values); + DEFAULT_TYPE_VALUES = Collections.unmodifiableMap(values); } - private static final Map wellKnownStaticFinals; + private static final Map WELL_KNOWN_STATIC_FINALS; static { Map values = new HashMap<>(); @@ -98,7 +98,22 @@ public class JavaCompilerFieldValuesParser implements FieldValuesParser { values.put("StandardCharsets.UTF_8", "UTF-8"); values.put("StandardCharsets.UTF_16", "UTF-16"); values.put("StandardCharsets.US_ASCII", "US-ASCII"); - wellKnownStaticFinals = Collections.unmodifiableMap(values); + WELL_KNOWN_STATIC_FINALS = Collections.unmodifiableMap(values); + } + + private static final String DURATION_OF = "Duration.of"; + + private static final Map DURATION_SUFFIX; + + static { + Map values = new HashMap<>(); + values.put("Nanos", "ns"); + values.put("Millis", "ms"); + values.put("Seconds", "s"); + values.put("Minutes", "m"); + values.put("Hours", "h"); + values.put("Days", "d"); + DURATION_SUFFIX = Collections.unmodifiableMap(values); } private final Map fieldValues = new HashMap<>(); @@ -119,7 +134,7 @@ public class JavaCompilerFieldValuesParser implements FieldValuesParser { private Object getValue(VariableTree variable) throws Exception { ExpressionTree initializer = variable.getInitializer(); Class wrapperType = WRAPPER_TYPES.get(variable.getType()); - Object defaultValue = defaultTypeValues.get(wrapperType); + Object defaultValue = DEFAULT_TYPE_VALUES.get(wrapperType); if (initializer != null) { return getValue(initializer, defaultValue); } @@ -134,7 +149,7 @@ public class JavaCompilerFieldValuesParser implements FieldValuesParser { } Object factoryValue = expression.getFactoryValue(); if (factoryValue != null) { - return factoryValue; + return getFactoryValue(expression, factoryValue); } List arrayValues = expression.getArrayExpression(); if (arrayValues != null) { @@ -152,11 +167,22 @@ public class JavaCompilerFieldValuesParser implements FieldValuesParser { return this.staticFinals.get(expression.toString()); } if (expression.getKind().equals("MEMBER_SELECT")) { - return wellKnownStaticFinals.get(expression.toString()); + return WELL_KNOWN_STATIC_FINALS.get(expression.toString()); } return defaultValue; } + private Object getFactoryValue(ExpressionTree expression, Object factoryValue) { + Object instance = expression.getInstance(); + if (instance != null && instance.toString().startsWith(DURATION_OF)) { + String type = instance.toString(); + type = type.substring(DURATION_OF.length(), type.indexOf("(")); + String suffix = DURATION_SUFFIX.get(type); + return (suffix == null ? null : factoryValue + suffix); + } + return factoryValue; + } + public Map getFieldValues() { return this.fieldValues; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java index bfafc3aba6..e190a20267 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java @@ -19,6 +19,7 @@ package org.springframework.boot.configurationprocessor; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.time.Duration; import java.util.Arrays; import java.util.Collections; @@ -532,8 +533,8 @@ public class ConfigurationMetadataAnnotationProcessorTests { @Test public void simpleEndpoint() throws IOException { ConfigurationMetadata metadata = compile(SimpleEndpoint.class); - assertThat(metadata).has( - Metadata.withGroup("management.endpoint.simple").fromSource(SimpleEndpoint.class)); + assertThat(metadata).has(Metadata.withGroup("management.endpoint.simple") + .fromSource(SimpleEndpoint.class)); assertThat(metadata).has(enabledFlag("simple", true)); assertThat(metadata).has(cacheTtl("simple")); assertThat(metadata.getItems()).hasSize(3); @@ -564,9 +565,9 @@ public class ConfigurationMetadataAnnotationProcessorTests { ConfigurationMetadata metadata = compile(CustomPropertiesEndpoint.class); assertThat(metadata).has(Metadata.withGroup("management.endpoint.customprops") .fromSource(CustomPropertiesEndpoint.class)); - assertThat(metadata).has(Metadata - .withProperty("management.endpoint.customprops.name") - .ofType(String.class).withDefaultValue("test")); + assertThat(metadata) + .has(Metadata.withProperty("management.endpoint.customprops.name") + .ofType(String.class).withDefaultValue("test")); assertThat(metadata).has(enabledFlag("customprops", true)); assertThat(metadata).has(cacheTtl("customprops")); assertThat(metadata.getItems()).hasSize(4); @@ -632,8 +633,8 @@ public class ConfigurationMetadataAnnotationProcessorTests { private Metadata.MetadataItemCondition cacheTtl(String endpointId) { return Metadata .withProperty("management.endpoint." + endpointId + ".cache.time-to-live") - .ofType(Long.class).withDefaultValue(0).withDescription( - "Maximum time in milliseconds that a response can be cached."); + .ofType(Duration.class).withDefaultValue(0) + .withDescription("Maximum time that a response can be cached."); } @Test diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java index fcc553f529..02e66206c4 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java @@ -88,6 +88,13 @@ public abstract class AbstractFieldValuesProcessorTests { .isEqualTo(new Object[] { "c" }); assertThat(values.get("integerArray")).isEqualTo(new Object[] { 42, 24 }); assertThat(values.get("unknownArray")).isNull(); + assertThat(values.get("durationNone")).isNull(); + assertThat(values.get("durationNanos")).isEqualTo("5ns"); + assertThat(values.get("durationMillis")).isEqualTo("10ms"); + assertThat(values.get("durationSeconds")).isEqualTo("20s"); + assertThat(values.get("durationMinutes")).isEqualTo("30m"); + assertThat(values.get("durationHours")).isEqualTo("40h"); + assertThat(values.get("durationDays")).isEqualTo("50d"); } @SupportedAnnotationTypes({ diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/fieldvalues/FieldValues.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/fieldvalues/FieldValues.java index 6f183f82f4..94d46e1d80 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/fieldvalues/FieldValues.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/fieldvalues/FieldValues.java @@ -18,6 +18,7 @@ package org.springframework.boot.configurationsample.fieldvalues; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.time.Duration; import org.springframework.boot.configurationsample.ConfigurationProperties; import org.springframework.util.MimeType; @@ -108,4 +109,18 @@ public class FieldValues { private FieldValues[] unknownArray = new FieldValues[] { new FieldValues() }; + private Duration durationNone; + + private Duration durationNanos = Duration.ofNanos(5); + + private Duration durationMillis = Duration.ofMillis(10); + + private Duration durationSeconds = Duration.ofSeconds(20); + + private Duration durationMinutes = Duration.ofMinutes(30); + + private Duration durationHours = Duration.ofHours(40); + + private Duration durationDays = Duration.ofDays(50); + }