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
pull/11063/head
Phillip Webb 7 years ago
parent 7c6c9ddf09
commit cbaf0fa686

@ -19,6 +19,7 @@ package org.springframework.boot.configurationprocessor;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.time.Duration;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -424,8 +425,8 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
String.format("Enable the %s endpoint.", endpointId), String.format("Enable the %s endpoint.", endpointId),
(enabledByDefault == null ? true : enabledByDefault), null)); (enabledByDefault == null ? true : enabledByDefault), null));
this.metadataCollector.add(ItemMetadata.newProperty(endpointKey(endpointId), this.metadataCollector.add(ItemMetadata.newProperty(endpointKey(endpointId),
"cache.time-to-live", Long.class.getName(), type, null, "cache.time-to-live", Duration.class.getName(), type, null,
"Maximum time in milliseconds that a response can be cached.", 0, null)); "Maximum time that a response can be cached.", 0, null));
} }
private String endpointKey(String suffix) { private String endpointKey(String suffix) {

@ -76,7 +76,7 @@ public class JavaCompilerFieldValuesParser implements FieldValuesParser {
WRAPPER_TYPES = Collections.unmodifiableMap(types); WRAPPER_TYPES = Collections.unmodifiableMap(types);
} }
private static final Map<Class<?>, Object> defaultTypeValues; private static final Map<Class<?>, Object> DEFAULT_TYPE_VALUES;
static { static {
Map<Class<?>, Object> values = new HashMap<>(); Map<Class<?>, Object> values = new HashMap<>();
@ -85,10 +85,10 @@ public class JavaCompilerFieldValuesParser implements FieldValuesParser {
values.put(Short.class, (short) 0); values.put(Short.class, (short) 0);
values.put(Integer.class, 0); values.put(Integer.class, 0);
values.put(Long.class, (long) 0); values.put(Long.class, (long) 0);
defaultTypeValues = Collections.unmodifiableMap(values); DEFAULT_TYPE_VALUES = Collections.unmodifiableMap(values);
} }
private static final Map<String, Object> wellKnownStaticFinals; private static final Map<String, Object> WELL_KNOWN_STATIC_FINALS;
static { static {
Map<String, Object> values = new HashMap<>(); Map<String, Object> values = new HashMap<>();
@ -98,7 +98,22 @@ public class JavaCompilerFieldValuesParser implements FieldValuesParser {
values.put("StandardCharsets.UTF_8", "UTF-8"); values.put("StandardCharsets.UTF_8", "UTF-8");
values.put("StandardCharsets.UTF_16", "UTF-16"); values.put("StandardCharsets.UTF_16", "UTF-16");
values.put("StandardCharsets.US_ASCII", "US-ASCII"); 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<String, String> DURATION_SUFFIX;
static {
Map<String, String> 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<String, Object> fieldValues = new HashMap<>(); private final Map<String, Object> fieldValues = new HashMap<>();
@ -119,7 +134,7 @@ public class JavaCompilerFieldValuesParser implements FieldValuesParser {
private Object getValue(VariableTree variable) throws Exception { private Object getValue(VariableTree variable) throws Exception {
ExpressionTree initializer = variable.getInitializer(); ExpressionTree initializer = variable.getInitializer();
Class<?> wrapperType = WRAPPER_TYPES.get(variable.getType()); Class<?> wrapperType = WRAPPER_TYPES.get(variable.getType());
Object defaultValue = defaultTypeValues.get(wrapperType); Object defaultValue = DEFAULT_TYPE_VALUES.get(wrapperType);
if (initializer != null) { if (initializer != null) {
return getValue(initializer, defaultValue); return getValue(initializer, defaultValue);
} }
@ -134,7 +149,7 @@ public class JavaCompilerFieldValuesParser implements FieldValuesParser {
} }
Object factoryValue = expression.getFactoryValue(); Object factoryValue = expression.getFactoryValue();
if (factoryValue != null) { if (factoryValue != null) {
return factoryValue; return getFactoryValue(expression, factoryValue);
} }
List<? extends ExpressionTree> arrayValues = expression.getArrayExpression(); List<? extends ExpressionTree> arrayValues = expression.getArrayExpression();
if (arrayValues != null) { if (arrayValues != null) {
@ -152,11 +167,22 @@ public class JavaCompilerFieldValuesParser implements FieldValuesParser {
return this.staticFinals.get(expression.toString()); return this.staticFinals.get(expression.toString());
} }
if (expression.getKind().equals("MEMBER_SELECT")) { if (expression.getKind().equals("MEMBER_SELECT")) {
return wellKnownStaticFinals.get(expression.toString()); return WELL_KNOWN_STATIC_FINALS.get(expression.toString());
} }
return defaultValue; 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<String, Object> getFieldValues() { public Map<String, Object> getFieldValues() {
return this.fieldValues; return this.fieldValues;
} }

@ -19,6 +19,7 @@ package org.springframework.boot.configurationprocessor;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.time.Duration;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -532,8 +533,8 @@ public class ConfigurationMetadataAnnotationProcessorTests {
@Test @Test
public void simpleEndpoint() throws IOException { public void simpleEndpoint() throws IOException {
ConfigurationMetadata metadata = compile(SimpleEndpoint.class); ConfigurationMetadata metadata = compile(SimpleEndpoint.class);
assertThat(metadata).has( assertThat(metadata).has(Metadata.withGroup("management.endpoint.simple")
Metadata.withGroup("management.endpoint.simple").fromSource(SimpleEndpoint.class)); .fromSource(SimpleEndpoint.class));
assertThat(metadata).has(enabledFlag("simple", true)); assertThat(metadata).has(enabledFlag("simple", true));
assertThat(metadata).has(cacheTtl("simple")); assertThat(metadata).has(cacheTtl("simple"));
assertThat(metadata.getItems()).hasSize(3); assertThat(metadata.getItems()).hasSize(3);
@ -564,9 +565,9 @@ public class ConfigurationMetadataAnnotationProcessorTests {
ConfigurationMetadata metadata = compile(CustomPropertiesEndpoint.class); ConfigurationMetadata metadata = compile(CustomPropertiesEndpoint.class);
assertThat(metadata).has(Metadata.withGroup("management.endpoint.customprops") assertThat(metadata).has(Metadata.withGroup("management.endpoint.customprops")
.fromSource(CustomPropertiesEndpoint.class)); .fromSource(CustomPropertiesEndpoint.class));
assertThat(metadata).has(Metadata assertThat(metadata)
.withProperty("management.endpoint.customprops.name") .has(Metadata.withProperty("management.endpoint.customprops.name")
.ofType(String.class).withDefaultValue("test")); .ofType(String.class).withDefaultValue("test"));
assertThat(metadata).has(enabledFlag("customprops", true)); assertThat(metadata).has(enabledFlag("customprops", true));
assertThat(metadata).has(cacheTtl("customprops")); assertThat(metadata).has(cacheTtl("customprops"));
assertThat(metadata.getItems()).hasSize(4); assertThat(metadata.getItems()).hasSize(4);
@ -632,8 +633,8 @@ public class ConfigurationMetadataAnnotationProcessorTests {
private Metadata.MetadataItemCondition cacheTtl(String endpointId) { private Metadata.MetadataItemCondition cacheTtl(String endpointId) {
return Metadata return Metadata
.withProperty("management.endpoint." + endpointId + ".cache.time-to-live") .withProperty("management.endpoint." + endpointId + ".cache.time-to-live")
.ofType(Long.class).withDefaultValue(0).withDescription( .ofType(Duration.class).withDefaultValue(0)
"Maximum time in milliseconds that a response can be cached."); .withDescription("Maximum time that a response can be cached.");
} }
@Test @Test

@ -88,6 +88,13 @@ public abstract class AbstractFieldValuesProcessorTests {
.isEqualTo(new Object[] { "c" }); .isEqualTo(new Object[] { "c" });
assertThat(values.get("integerArray")).isEqualTo(new Object[] { 42, 24 }); assertThat(values.get("integerArray")).isEqualTo(new Object[] { 42, 24 });
assertThat(values.get("unknownArray")).isNull(); 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({ @SupportedAnnotationTypes({

@ -18,6 +18,7 @@ package org.springframework.boot.configurationsample.fieldvalues;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Duration;
import org.springframework.boot.configurationsample.ConfigurationProperties; import org.springframework.boot.configurationsample.ConfigurationProperties;
import org.springframework.util.MimeType; import org.springframework.util.MimeType;
@ -108,4 +109,18 @@ public class FieldValues {
private FieldValues[] unknownArray = new FieldValues[] { new 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);
} }

Loading…
Cancel
Save