diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 782a13e319bf..d5da27ef2fae 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -133,6 +133,10 @@ "description": "Whether to enable disk space health check.", "defaultValue": true }, + { + "name": "management.health.diskspace.path", + "defaultValue": "." + }, { "name": "management.health.elasticsearch.enabled", "type": "java.lang.Boolean", @@ -462,6 +466,10 @@ "level": "error" } }, + { + "name": "management.metrics.export.newrelic.client-provider-type", + "defaultValue": "INSIGHTS_API" + }, { "name": "management.metrics.export.prometheus.enabled", "type": "java.lang.Boolean", @@ -540,6 +548,10 @@ "level": "error" } }, + { + "name": "management.metrics.export.wavefront.sender.message-size", + "defaultValue": "2147483647B" + }, { "name": "management.metrics.mongo.command.enabled", "description": "Whether to enable Mongo client command metrics.", diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 170b9e0ce574..c8df82736db7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -250,6 +250,10 @@ "level": "error" } }, + { + "name": "server.undertow.accesslog.dir", + "defaultValue": "logs" + }, { "name": "server.undertow.buffers-per-region", "type": "java.lang.Integer", @@ -258,6 +262,14 @@ "level": "error" } }, + { + "name": "server.undertow.max-headers", + "defaultValue": 200 + }, + { + "name": "server.undertow.max-parameters", + "defaultValue": 1000 + }, { "name": "server.use-forward-headers", "type": "java.lang.Boolean", @@ -336,6 +348,14 @@ "level": "error" } }, + { + "name": "spring.artemis.embedded.cluster-password", + "defaultValue": "UUID.randomUUID().toString()" + }, + { + "name": "spring.artemis.embedded.server-id", + "defaultValue": 0 + }, { "name": "spring.artemis.pool.maximum-active-session-per-connection", "deprecation": { @@ -1088,10 +1108,22 @@ "name": "spring.info.git.location", "defaultValue": "classpath:git.properties" }, + { + "name": "spring.integration.channel.max-broadcast-subscribers", + "defaultValue": 2147483647 + }, + { + "name": "spring.integration.channel.max-unicast-subscribers", + "defaultValue": 2147483647 + }, { "name": "spring.integration.jdbc.initialize-schema", "defaultValue": "embedded" }, + { + "name": "spring.integration.poller.max-messages-per-poll", + "defaultValue": "-2147483648" + }, { "name": "spring.jackson.constructor-detector", "defaultValue": "default" @@ -1459,10 +1491,20 @@ "level": "error" } }, + { + "name": "spring.kafka.bootstrap-servers", + "defaultValue": [ + "localhost:9092" + ] + }, { "name": "spring.kafka.consumer.isolation-level", "defaultValue": "read-uncommitted" }, + { + "name": "spring.kafka.consumer.key-deserializer", + "defaultValue": "org.apache.kafka.common.serialization.StringDeserializer" + }, { "name": "spring.kafka.consumer.ssl.keystore-location", "type": "org.springframework.core.io.Resource", @@ -1499,6 +1541,10 @@ "level": "error" } }, + { + "name": "spring.kafka.consumer.value-deserializer", + "defaultValue": "org.apache.kafka.common.serialization.StringDeserializer" + }, { "name": "spring.kafka.jaas.control-flag", "defaultValue": "required" @@ -1507,6 +1553,10 @@ "name": "spring.kafka.listener.type", "defaultValue": "single" }, + { + "name": "spring.kafka.producer.key-serializer", + "defaultValue": "org.apache.kafka.common.serialization.StringSerializer" + }, { "name": "spring.kafka.producer.ssl.keystore-location", "type": "org.springframework.core.io.Resource", @@ -1543,6 +1593,10 @@ "level": "error" } }, + { + "name": "spring.kafka.producer.value-serializer", + "defaultValue": "org.apache.kafka.common.serialization.StringSerializer" + }, { "name": "spring.kafka.ssl.keystore-location", "type": "org.springframework.core.io.Resource", @@ -1736,6 +1790,10 @@ "level": "error" } }, + { + "name": "spring.rabbitmq.stream.port", + "defaultValue": 5552 + }, { "name": "spring.rabbitmq.template.queue", "type": "java.lang.String", @@ -1967,6 +2025,10 @@ "name": "spring.security.filter.order", "defaultValue": -100 }, + { + "name": "spring.security.user.password", + "defaultValue": "UUID.randomUUID().toString()" + }, { "name": "spring.session.hazelcast.flush-mode", "defaultValue": "on-save" @@ -2007,6 +2069,10 @@ "request" ] }, + { + "name": "spring.session.servlet.filter-order", + "defaultValue": -2147483598 + }, { "name": "spring.sql.init.enabled", "type": "java.lang.Boolean", @@ -2021,6 +2087,13 @@ "name": "spring.sql.init.mode", "defaultValue": "embedded" }, + { + "name": "spring.task.execution.pool.max-size", + "defaultValue": 2147483647 + }, { + "name": "spring.task.execution.pool.queue-capacity", + "defaultValue": 2147483647 + }, { "name": "spring.web.locale-resolver", "defaultValue": "accept-header" 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 7cc52dfac797..c0d8e1cce02b 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 @@ -45,6 +45,7 @@ import javax.lang.model.util.ElementFilter; import javax.tools.Diagnostic.Kind; +import org.springframework.boot.configurationprocessor.fieldvalues.ValueWrapper; import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; import org.springframework.boot.configurationprocessor.metadata.InvalidConfigurationMetadataException; import org.springframework.boot.configurationprocessor.metadata.ItemMetadata; @@ -57,6 +58,7 @@ * @author Phillip Webb * @author Kris De Volder * @author Jonas Keßler + * @author Chris Bono * @since 1.2.0 */ @SupportedAnnotationTypes({ ConfigurationMetadataAnnotationProcessor.CONFIGURATION_PROPERTIES_ANNOTATION, @@ -71,6 +73,8 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor static final String ADDITIONAL_METADATA_LOCATIONS_OPTION = "org.springframework.boot.configurationprocessor.additionalMetadataLocations"; + static final String FAIL_ON_UNDETERMINED_DEFAULT_VALUE_OPTION = "org.springframework.boot.configurationprocessor.failOnUndeterminedDefaultValue"; + static final String CONFIGURATION_PROPERTIES_ANNOTATION = "org.springframework.boot.context.properties.ConfigurationProperties"; static final String NESTED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot.context.properties.NestedConfigurationProperty"; @@ -97,8 +101,8 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor static final String NAME_ANNOTATION = "org.springframework.boot.context.properties.bind.Name"; - private static final Set SUPPORTED_OPTIONS = Collections - .unmodifiableSet(Collections.singleton(ADDITIONAL_METADATA_LOCATIONS_OPTION)); + private static final Set SUPPORTED_OPTIONS = Collections.unmodifiableSet(new HashSet<>( + Arrays.asList(ADDITIONAL_METADATA_LOCATIONS_OPTION, FAIL_ON_UNDETERMINED_DEFAULT_VALUE_OPTION))); private MetadataStore metadataStore; @@ -328,6 +332,7 @@ protected ConfigurationMetadata writeMetaData() throws Exception { ConfigurationMetadata metadata = this.metadataCollector.getMetadata(); metadata = mergeAdditionalMetadata(metadata); if (!metadata.getItems().isEmpty()) { + validateDefaultValuesResolved(metadata); this.metadataStore.writeMetadata(metadata); return metadata; } @@ -353,6 +358,26 @@ private ConfigurationMetadata mergeAdditionalMetadata(ConfigurationMetadata meta return metadata; } + private void validateDefaultValuesResolved(ConfigurationMetadata metadata) { + + boolean failOnBadDefaultValues = Boolean.valueOf(this.processingEnv.getOptions().getOrDefault( + ConfigurationMetadataAnnotationProcessor.FAIL_ON_UNDETERMINED_DEFAULT_VALUE_OPTION, "true")); + + metadata.getItems().forEach((itemMetadata) -> { + if (itemMetadata.getDefaultValue() instanceof ValueWrapper) { + ValueWrapper defaultValue = (ValueWrapper) itemMetadata.getDefaultValue(); + if (!defaultValue.valueDetermined()) { + String msg = String.format( + "Could not resolve default value for property '%s' using expression '%s'. Add 'defaultValue' in additional-spring-configuration.metadata.json", + itemMetadata.getName(), defaultValue.valueInitializerExpression()); + log(failOnBadDefaultValues ? Kind.ERROR : Kind.WARNING, msg); + } + itemMetadata.setDefaultValue(defaultValue.value()); + } + + }); + } + private String getStackTrace(Exception ex) { StringWriter writer = new StringWriter(); ex.printStackTrace(new PrintWriter(writer, true)); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/FieldValuesParser.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/FieldValuesParser.java index 2032cc64c8d1..c969026118c4 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/FieldValuesParser.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/FieldValuesParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -27,6 +27,7 @@ * Parser which can be used to obtain the field values from an {@link TypeElement}. * * @author Phillip Webb + * @author Chris Bono * @since 1.1.2 * @see JavaCompilerFieldValuesParser */ @@ -42,8 +43,8 @@ public interface FieldValuesParser { * Return the field values for the given element. * @param element the element to inspect * @return a map of field names to values. - * @throws Exception if the values cannot be extracted + * @throws Exception if extraction fails unexpectedly */ - Map getFieldValues(TypeElement element) throws Exception; + Map getFieldValues(TypeElement element) throws Exception; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/ValueWrapper.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/ValueWrapper.java new file mode 100644 index 000000000000..0733a3ac7633 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/ValueWrapper.java @@ -0,0 +1,104 @@ +/* + * Copyright 2012-2021 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationprocessor.fieldvalues; + +import java.util.Objects; + +import javax.lang.model.element.TypeElement; + +/** + * The value assigned to a field in a {@link TypeElement}, including information related + * to value resolution. + * + * @author Chris Bono + * @since 2.6 + */ +public final class ValueWrapper { + + private final Object value; + + private final String valueInitializerExpression; + + private final boolean valueDetermined; + + private ValueWrapper(Object value, String valueInitializerExpression, boolean valueDetermined) { + this.value = value; + this.valueInitializerExpression = valueInitializerExpression; + this.valueDetermined = valueDetermined; + } + + public static ValueWrapper of(Object value, String valueInitializerExpression) { + return new ValueWrapper(value, valueInitializerExpression, true); + } + + public static ValueWrapper unresolvable(String valueInitializerExpression) { + return new ValueWrapper(null, valueInitializerExpression, false); + } + + /** + * Return the wrapped value. + * @return the wrapped value or {@code null} if the value was initialized with + * {@code null} or the initializer expression was {@link #valueDetermined() unable to + * be determined}. + */ + public Object value() { + return this.value; + } + + /** + * Return the expression used to initialize the value. + * @return the expression used to initialize the value or {@code null} if no + * initializer was used to determine the value + */ + public String valueInitializerExpression() { + return this.valueInitializerExpression; + } + + /** + * Return whether or not the value was able to be determined. + * @return {@code true} if the value was able to be determined, {@code false} + * otherwise. + */ + public boolean valueDetermined() { + return this.valueDetermined; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ValueWrapper other = (ValueWrapper) o; + return this.valueDetermined == other.valueDetermined && Objects.deepEquals(this.value, other.value) + && Objects.equals(this.valueInitializerExpression, other.valueInitializerExpression); + } + + @Override + public int hashCode() { + return Objects.hash(this.value, this.valueInitializerExpression, this.valueDetermined); + } + + @Override + public String toString() { + return "ValueWrapper{value=" + this.value + ", valueInitializerExpression=" + this.valueInitializerExpression + + ", valueDetermined=" + this.valueDetermined + "}"; + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/javac/ExpressionTree.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/javac/ExpressionTree.java index 9f4b5a23dea4..3a97a2038fa1 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/javac/ExpressionTree.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/javac/ExpressionTree.java @@ -36,6 +36,12 @@ class ExpressionTree extends ReflectionWrapper { private final Method methodInvocationArgumentsMethod = findMethod(this.methodInvocationTreeType, "getArguments"); + private final Class newClassTreeType = findClass("com.sun.source.tree.NewClassTree"); + + private final Method newClassArgumentsMethod = findMethod(this.newClassTreeType, "getArguments"); + + private final Method newClassIdentifierMethod = findMethod(this.newClassTreeType, "getIdentifier"); + private final Class newArrayTreeType = findClass("com.sun.source.tree.NewArrayTree"); private final Method arrayValueMethod = findMethod(this.newArrayTreeType, "getInitializers"); @@ -48,9 +54,20 @@ String getKind() throws Exception { return findMethod("getKind").invoke(getInstance()).toString(); } + boolean hasLiteralValue() { + return this.literalTreeType.isAssignableFrom(getInstance().getClass()); + } + Object getLiteralValue() throws Exception { - if (this.literalTreeType.isAssignableFrom(getInstance().getClass())) { - return this.literalValueMethod.invoke(getInstance()); + return hasLiteralValue() ? this.literalValueMethod.invoke(getInstance()) : null; + } + + Object getIdentifierForNewClassNoArgConstructor() throws Exception { + if (this.newClassTreeType.isAssignableFrom(getInstance().getClass())) { + List arguments = (List) this.newClassArgumentsMethod.invoke(getInstance()); + if (arguments.isEmpty()) { + return this.newClassIdentifierMethod.invoke(getInstance()); + } } return null; } 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 7e574f1db331..7669f277eede 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -21,22 +21,29 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import org.springframework.boot.configurationprocessor.fieldvalues.FieldValuesParser; +import org.springframework.boot.configurationprocessor.fieldvalues.ValueWrapper; /** * {@link FieldValuesParser} implementation for the standard Java compiler. * * @author Phillip Webb * @author Stephane Nicoll + * @author Chris Bono * @since 1.2.0 */ public class JavaCompilerFieldValuesParser implements FieldValuesParser { + private static final Pattern COLLECTION_PATTERN = Pattern.compile("^.*(List|Set)<.*>$"); + + private static final Pattern MAP_PATTERN = Pattern.compile("^.*(Map)<.*>$"); + private final Trees trees; public JavaCompilerFieldValuesParser(ProcessingEnvironment env) throws Exception { @@ -44,7 +51,7 @@ public JavaCompilerFieldValuesParser(ProcessingEnvironment env) throws Exception } @Override - public Map getFieldValues(TypeElement element) throws Exception { + public Map getFieldValues(TypeElement element) throws Exception { Tree tree = this.trees.getTree(element); if (tree != null) { FieldCollector fieldCollector = new FieldCollector(); @@ -145,9 +152,9 @@ private static class FieldCollector implements TreeVisitor { DATA_SIZE_SUFFIX = Collections.unmodifiableMap(values); } - private final Map fieldValues = new HashMap<>(); + private final Map fieldValues = new HashMap<>(); - private final Map staticFinals = new HashMap<>(); + private final Map staticFinals = new HashMap<>(); @Override public void visitVariable(VariableTree variable) throws Exception { @@ -160,44 +167,77 @@ public void visitVariable(VariableTree variable) throws Exception { } } - private Object getValue(VariableTree variable) throws Exception { + private ValueWrapper getValue(VariableTree variable) throws Exception { ExpressionTree initializer = variable.getInitializer(); - Class wrapperType = WRAPPER_TYPES.get(variable.getType()); - Object defaultValue = DEFAULT_TYPE_VALUES.get(wrapperType); if (initializer != null) { - return getValue(initializer, defaultValue); + return getValue(initializer); + } + Class wrapperType = WRAPPER_TYPES.get(variable.getType()); + if (DEFAULT_TYPE_VALUES.containsKey(wrapperType)) { + Object defaultValue = DEFAULT_TYPE_VALUES.get(wrapperType); + return ValueWrapper.of(defaultValue, null); } - return defaultValue; + return null; } - private Object getValue(ExpressionTree expression, Object defaultValue) throws Exception { - Object literalValue = expression.getLiteralValue(); - if (literalValue != null) { - return literalValue; + private ValueWrapper getValue(ExpressionTree expression) throws Exception { + if (expression.hasLiteralValue()) { + Object literalValue = expression.getLiteralValue(); + return ValueWrapper.of(literalValue, expression.toString()); } + Object factoryValue = expression.getFactoryValue(); if (factoryValue != null) { - return getFactoryValue(expression, factoryValue); + Object resolvedValue = getFactoryValue(expression, factoryValue); + if (resolvedValue != null) { + return ValueWrapper.of(resolvedValue, expression.toString()); + } + return ValueWrapper.unresolvable(expression.toString()); } + List arrayValues = expression.getArrayExpression(); if (arrayValues != null) { Object[] result = new Object[arrayValues.size()]; for (int i = 0; i < arrayValues.size(); i++) { - Object value = getValue(arrayValues.get(i), null); - if (value == null) { // One of the elements could not be resolved - return defaultValue; + ValueWrapper valueWrapper = getValue(arrayValues.get(i)); + if (!valueWrapper.valueDetermined()) { // an element could not be + // resolved + return ValueWrapper.unresolvable(arrayValues.get(i).toString()); } - result[i] = value; + result[i] = valueWrapper.value(); } - return result; + return ValueWrapper.of(result, expression.toString()); } + + Object identifierForNewClass = expression.getIdentifierForNewClassNoArgConstructor(); + if (identifierForNewClass != null) { + String classIdentifier = identifierForNewClass.toString(); + if (COLLECTION_PATTERN.matcher(classIdentifier).matches()) { + return ValueWrapper.of("[]", expression.toString()); + } + if (MAP_PATTERN.matcher(classIdentifier).matches()) { + return ValueWrapper.of("{}", expression.toString()); + } + } + if (expression.getKind().equals("IDENTIFIER")) { - return this.staticFinals.get(expression.toString()); + String expressionStr = expression.toString(); + if (this.staticFinals.containsKey(expressionStr)) { + return this.staticFinals.get(expressionStr); + } + return ValueWrapper.unresolvable(expressionStr); } + if (expression.getKind().equals("MEMBER_SELECT")) { - return WELL_KNOWN_STATIC_FINALS.get(expression.toString()); + String expressionStr = expression.toString(); + if (WELL_KNOWN_STATIC_FINALS.containsKey(expressionStr)) { + Object resolvedValue = WELL_KNOWN_STATIC_FINALS.get(expressionStr); + return ValueWrapper.of(resolvedValue, expressionStr); + } + return ValueWrapper.unresolvable(expressionStr); } - return defaultValue; + + return ValueWrapper.unresolvable(expression.toString()); } private Object getFactoryValue(ExpressionTree expression, Object factoryValue) { @@ -228,7 +268,7 @@ private Object getFactoryValue(ExpressionTree expression, Object factoryValue, S return null; } - Map getFieldValues() { + 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/AbstractMetadataGenerationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/AbstractMetadataGenerationTests.java index 55567a17a878..4f99adbb03b7 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/AbstractMetadataGenerationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/AbstractMetadataGenerationTests.java @@ -55,6 +55,13 @@ protected ConfigurationMetadata compile(Class... types) { return processor.getMetadata(); } + protected ConfigurationMetadata compileWithOptions(Iterable options, Class... types) { + TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor( + this.compiler.getOutputLocation()); + this.compiler.getTaskWithOptions(options, types).call(processor); + return processor.getMetadata(); + } + protected ConfigurationMetadata compile(File... sources) { TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor( this.compiler.getOutputLocation()); 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 756d4b9a4ca5..e70380aea21f 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 @@ -20,6 +20,8 @@ import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; +import java.util.Arrays; +import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledForJreRange; @@ -39,6 +41,7 @@ import org.springframework.boot.configurationsample.simple.HierarchicalPropertiesParent; import org.springframework.boot.configurationsample.simple.NotAnnotated; import org.springframework.boot.configurationsample.simple.SimpleArrayProperties; +import org.springframework.boot.configurationsample.simple.SimpleBadDefaultProperties; import org.springframework.boot.configurationsample.simple.SimpleCollectionProperties; import org.springframework.boot.configurationsample.simple.SimplePrefixValueProperties; import org.springframework.boot.configurationsample.simple.SimpleProperties; @@ -65,6 +68,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * Tests for {@link ConfigurationMetadataAnnotationProcessor}. @@ -110,6 +114,30 @@ void simpleProperties() { assertThat(metadata).doesNotHave(Metadata.withProperty("simple.size")); } + @Test + void simplePropertiesWithUnresolvableDefaultValueLogsErrorByDefault() { + assertThatThrownBy(() -> compile(SimpleBadDefaultProperties.class)).isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Compilation failed"); + } + + @Test + void simplePropertiesWithUnresolvableDefaultValueLogsErrorWhenOptionSetToTrue() { + List options = Arrays + .asList("-Aorg.springframework.boot.configurationprocessor.failOnUndeterminedDefaultValue=true"); + assertThatThrownBy(() -> compileWithOptions(options, SimpleBadDefaultProperties.class)) + .isInstanceOf(IllegalStateException.class).hasMessageContaining("Compilation failed"); + } + + @Test + void simplePropertiesWithUnresolvableDefaultsLogsWarnWhenOptionSetToFalse() { + List options = Arrays + .asList("-Aorg.springframework.boot.configurationprocessor.failOnUndeterminedDefaultValue=false"); + ConfigurationMetadata metadata = compileWithOptions(options, SimpleBadDefaultProperties.class); + assertThat(metadata) + .has(Metadata.withProperty("simple.bad.default.some-list", "java.util.List") + .withDefaultValue(null)); + } + @Test void simplePrefixValueProperties() { ConfigurationMetadata metadata = compile(SimplePrefixValueProperties.class); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/JavaBeanPropertyDescriptorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/JavaBeanPropertyDescriptorTests.java index a4ced722c79a..514c696d1ec7 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/JavaBeanPropertyDescriptorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/JavaBeanPropertyDescriptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test; +import org.springframework.boot.configurationprocessor.fieldvalues.ValueWrapper; import org.springframework.boot.configurationsample.simple.DeprecatedSingleProperty; import org.springframework.boot.configurationsample.simple.SimpleCollectionProperties; import org.springframework.boot.configurationsample.simple.SimpleProperties; @@ -224,7 +225,7 @@ void javaBeanPropertyWithDefaultValue() throws IOException { process(SimpleProperties.class, (roundEnv, metadataEnv) -> { TypeElement ownerElement = roundEnv.getRootElement(SimpleProperties.class); JavaBeanPropertyDescriptor property = createPropertyDescriptor(ownerElement, "theName"); - assertItemMetadata(metadataEnv, property).isProperty().hasDefaultValue("boot"); + assertItemMetadata(metadataEnv, property).isProperty().hasDefaultValue(ValueWrapper.of("boot", "\"boot\"")); }); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/LombokPropertyDescriptorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/LombokPropertyDescriptorTests.java index 66d02ce82fb2..cd37218eb49b 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/LombokPropertyDescriptorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/LombokPropertyDescriptorTests.java @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test; +import org.springframework.boot.configurationprocessor.fieldvalues.ValueWrapper; import org.springframework.boot.configurationsample.lombok.LombokDefaultValueProperties; import org.springframework.boot.configurationsample.lombok.LombokDeprecatedSingleProperty; import org.springframework.boot.configurationsample.lombok.LombokExplicitProperties; @@ -237,7 +238,8 @@ void lombokPropertyWithDefaultValue() throws IOException { process(LombokDefaultValueProperties.class, (roundEnv, metadataEnv) -> { TypeElement ownerElement = roundEnv.getRootElement(LombokDefaultValueProperties.class); LombokPropertyDescriptor property = createPropertyDescriptor(ownerElement, "description"); - assertItemMetadata(metadataEnv, property).isProperty().hasDefaultValue("my description"); + assertItemMetadata(metadataEnv, property).isProperty() + .hasDefaultValue(ValueWrapper.of("my description", "\"my description\"")); }); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MergeMetadataGenerationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MergeMetadataGenerationTests.java index 7b439437c80c..dcf74476ac6a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MergeMetadataGenerationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/MergeMetadataGenerationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -35,17 +35,20 @@ import org.springframework.boot.configurationprocessor.metadata.Metadata; import org.springframework.boot.configurationprocessor.metadata.TestJsonConverter; import org.springframework.boot.configurationsample.simple.DeprecatedSingleProperty; +import org.springframework.boot.configurationsample.simple.SimpleBadDefaultProperties; import org.springframework.boot.configurationsample.simple.SimpleProperties; import org.springframework.boot.configurationsample.specific.SimpleConflictingProperties; import org.springframework.util.FileCopyUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * Metadata generation tests for merging additional metadata. * * @author Stephane Nicoll + * @author Chris Bono */ class MergeMetadataGenerationTests extends AbstractMetadataGenerationTests { @@ -79,6 +82,24 @@ void mergeExistingPropertyDefaultValue() throws Exception { assertThat(metadata.getItems()).hasSize(4); } + @Test + void fixingOfFailedCompileWithAdditionalDefaultValue() throws Exception { + // Fails w/ undetermined default value + assertThatThrownBy(() -> compile(SimpleBadDefaultProperties.class)).isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Compilation failed"); + + // Add default value to additional metadata + ItemMetadata property = ItemMetadata.newProperty("simple.bad.default", "some-list", null, null, null, null, + "[ \"a\", \"b\", \"c\" ]", null); + writeAdditionalMetadata(property); + + // Succeeds w/ merged default value + ConfigurationMetadata metadata = compile(SimpleBadDefaultProperties.class); + assertThat(metadata) + .has(Metadata.withProperty("simple.bad.default.some-list", "java.util.List") + .withDefaultValue("[ \"a\", \"b\", \"c\" ]")); + } + @Test void mergeExistingPropertyWithSeveralCandidates() throws Exception { ItemMetadata property = ItemMetadata.newProperty("simple", "flag", Boolean.class.getName(), null, null, null, diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolverTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolverTests.java index 69b4f051ffac..1f3070d8e5c9 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolverTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/PropertyDescriptorResolverTests.java @@ -31,6 +31,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.springframework.boot.configurationprocessor.fieldvalues.ValueWrapper; import org.springframework.boot.configurationprocessor.metadata.ItemMetadata; import org.springframework.boot.configurationprocessor.test.RoundEnvironmentTester; import org.springframework.boot.configurationprocessor.test.TestableAnnotationProcessor; @@ -83,7 +84,8 @@ void propertiesWithJavaBeanHierarchicalProperties() throws IOException { "HierarchicalPropertiesParent"); assertThat(resolver.resolve(type, null) .map((descriptor) -> descriptor.resolveItemMetadata("test", metadataEnv)) - .map(ItemMetadata::getDefaultValue)).containsExactly("three", "two", "one"); + .map(ItemMetadata::getDefaultValue)).containsExactly(ValueWrapper.of("three", "\"three\""), + ValueWrapper.of("two", "\"two\""), ValueWrapper.of("one", "\"one\"")); }); } 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 883d2542b39d..64f37c6dac3d 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -43,6 +43,7 @@ * * @author Phillip Webb * @author Stephane Nicoll + * @author Chris Bono */ public abstract class AbstractFieldValuesProcessorTests { @@ -56,57 +57,82 @@ void getFieldValues() throws Exception { TestProcessor processor = new TestProcessor(); TestCompiler compiler = new TestCompiler(this.tempDir); compiler.getTask(FieldValues.class).call(processor); - Map values = processor.getValues(); - assertThat(values.get("string")).isEqualTo("1"); + + Map values = processor.getValues(); + + assertThat(values.get("string")).isEqualTo(ValueWrapper.of("1", "\"1\"")); assertThat(values.get("stringNone")).isNull(); - assertThat(values.get("stringConst")).isEqualTo("c"); - assertThat(values.get("bool")).isEqualTo(true); - assertThat(values.get("boolNone")).isEqualTo(false); - assertThat(values.get("boolConst")).isEqualTo(true); - assertThat(values.get("boolObject")).isEqualTo(true); + assertThat(values.get("stringNull")).isEqualTo(ValueWrapper.of(null, "null")); + assertThat(values.get("stringConst")).isEqualTo(ValueWrapper.of("c", "\"c\"")); + assertThat(values.get("stringConstNull")).isEqualTo(ValueWrapper.of(null, "null")); + assertThat(values.get("stringFinal")).isEqualTo(ValueWrapper.unresolvable("this.STRING_FINAL")); + assertThat(values.get("bool")).isEqualTo(ValueWrapper.of(true, "true")); + assertThat(values.get("boolNone")).isEqualTo(ValueWrapper.of(false, null)); + assertThat(values.get("boolConst")).isEqualTo(ValueWrapper.of(true, "true")); + assertThat(values.get("boolObject")).isEqualTo(ValueWrapper.of(true, "Boolean.TRUE")); assertThat(values.get("boolObjectNone")).isNull(); - assertThat(values.get("boolObjectConst")).isEqualTo(true); - assertThat(values.get("integer")).isEqualTo(1); - assertThat(values.get("integerNone")).isEqualTo(0); - assertThat(values.get("integerConst")).isEqualTo(2); - assertThat(values.get("integerObject")).isEqualTo(3); + assertThat(values.get("boolObjectConst")).isEqualTo(ValueWrapper.of(true, "true")); + assertThat(values.get("integer")).isEqualTo(ValueWrapper.of(1, "1")); + assertThat(values.get("integerNone")).isEqualTo(ValueWrapper.of(0, null)); + assertThat(values.get("integerConst")).isEqualTo(ValueWrapper.of(2, "2")); + assertThat(values.get("integerObject")).isEqualTo(ValueWrapper.of(3, "3")); assertThat(values.get("integerObjectNone")).isNull(); - assertThat(values.get("integerObjectConst")).isEqualTo(4); - assertThat(values.get("charset")).isEqualTo("US-ASCII"); - assertThat(values.get("charsetConst")).isEqualTo("UTF-8"); - assertThat(values.get("mimeType")).isEqualTo("text/html"); - assertThat(values.get("mimeTypeConst")).isEqualTo("text/plain"); - assertThat(values.get("object")).isEqualTo(123); + assertThat(values.get("integerObjectConst")).isEqualTo(ValueWrapper.of(4, "4")); + assertThat(values.get("charset")).isEqualTo(ValueWrapper.of("US-ASCII", "StandardCharsets.US_ASCII")); + assertThat(values.get("charsetConst")).isEqualTo(ValueWrapper.of("UTF-8", "StandardCharsets.UTF_8")); + assertThat(values.get("mimeType")).isEqualTo(ValueWrapper.of("text/html", "MimeType.valueOf(\"text/html\")")); + assertThat(values.get("mimeTypeConst")) + .isEqualTo(ValueWrapper.of("text/plain", "MimeType.valueOf(\"text/plain\")")); + assertThat(values.get("object")).isEqualTo(ValueWrapper.of(123, "123")); assertThat(values.get("objectNone")).isNull(); - assertThat(values.get("objectConst")).isEqualTo("c"); - assertThat(values.get("objectInstance")).isNull(); - assertThat(values.get("stringArray")).isEqualTo(new Object[] { "FOO", "BAR" }); + assertThat(values.get("objectConst")).isEqualTo(ValueWrapper.of("c", "\"c\"")); + assertThat(values.get("objectInstance")).isEqualTo(ValueWrapper.unresolvable("new StringBuffer()")); + assertThat(values.get("stringArray")) + .isEqualTo(ValueWrapper.of(new String[] { "FOO", "BAR" }, "new String[]{\"FOO\", \"BAR\"}")); assertThat(values.get("stringArrayNone")).isNull(); - assertThat(values.get("stringEmptyArray")).isEqualTo(new Object[0]); - assertThat(values.get("stringArrayConst")).isEqualTo(new Object[] { "OK", "KO" }); - assertThat(values.get("stringArrayConstElements")).isEqualTo(new Object[] { "c" }); - assertThat(values.get("integerArray")).isEqualTo(new Object[] { 42, 24 }); - assertThat(values.get("unknownArray")).isNull(); + assertThat(values.get("stringEmptyArray")).isEqualTo(ValueWrapper.of(new String[0], "new String[0]")); + assertThat(values.get("stringArrayConst")) + .isEqualTo(ValueWrapper.of(new String[] { "OK", "KO" }, "new String[]{\"OK\", \"KO\"}")); + assertThat(values.get("stringArrayConstElements")) + .isEqualTo(ValueWrapper.of(new String[] { "c" }, "new String[]{STRING_CONST}")); + assertThat(values.get("integerArray")) + .isEqualTo(ValueWrapper.of(new Integer[] { 42, 24 }, "new Integer[]{42, 24}")); + assertThat(values.get("objectArrayBadEntry")).isEqualTo(ValueWrapper.unresolvable("new StringBuffer()")); + assertThat(values.get("unknownArray")).isEqualTo(ValueWrapper.unresolvable("new UnknownElementType()")); 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"); - assertThat(values.get("durationZero")).isEqualTo(0); + assertThat(values.get("durationNanos")).isEqualTo(ValueWrapper.of("5ns", "Duration.ofNanos(5)")); + assertThat(values.get("durationMillis")).isEqualTo(ValueWrapper.of("10ms", "Duration.ofMillis(10)")); + assertThat(values.get("durationSeconds")).isEqualTo(ValueWrapper.of("20s", "Duration.ofSeconds(20)")); + assertThat(values.get("durationMinutes")).isEqualTo(ValueWrapper.of("30m", "Duration.ofMinutes(30)")); + assertThat(values.get("durationHours")).isEqualTo(ValueWrapper.of("40h", "Duration.ofHours(40)")); + assertThat(values.get("durationDays")).isEqualTo(ValueWrapper.of("50d", "Duration.ofDays(50)")); + assertThat(values.get("durationZero")).isEqualTo(ValueWrapper.of(0, "Duration.ZERO")); assertThat(values.get("dataSizeNone")).isNull(); - assertThat(values.get("dataSizeBytes")).isEqualTo("5B"); - assertThat(values.get("dataSizeKilobytes")).isEqualTo("10KB"); - assertThat(values.get("dataSizeMegabytes")).isEqualTo("20MB"); - assertThat(values.get("dataSizeGigabytes")).isEqualTo("30GB"); - assertThat(values.get("dataSizeTerabytes")).isEqualTo("40TB"); + assertThat(values.get("dataSizeBytes")).isEqualTo(ValueWrapper.of("5B", "DataSize.ofBytes(5)")); + assertThat(values.get("dataSizeKilobytes")).isEqualTo(ValueWrapper.of("10KB", "DataSize.ofKilobytes(10)")); + assertThat(values.get("dataSizeMegabytes")).isEqualTo(ValueWrapper.of("20MB", "DataSize.ofMegabytes(20)")); + assertThat(values.get("dataSizeGigabytes")).isEqualTo(ValueWrapper.of("30GB", "DataSize.ofGigabytes(30)")); + assertThat(values.get("dataSizeTerabytes")).isEqualTo(ValueWrapper.of("40TB", "DataSize.ofTerabytes(40)")); assertThat(values.get("periodNone")).isNull(); - assertThat(values.get("periodDays")).isEqualTo("3d"); - assertThat(values.get("periodWeeks")).isEqualTo("2w"); - assertThat(values.get("periodMonths")).isEqualTo("10m"); - assertThat(values.get("periodYears")).isEqualTo("15y"); - assertThat(values.get("periodZero")).isEqualTo(0); + assertThat(values.get("periodDays")).isEqualTo(ValueWrapper.of("3d", "Period.ofDays(3)")); + assertThat(values.get("periodWeeks")).isEqualTo(ValueWrapper.of("2w", "Period.ofWeeks(2)")); + assertThat(values.get("periodMonths")).isEqualTo(ValueWrapper.of("10m", "Period.ofMonths(10)")); + assertThat(values.get("periodYears")).isEqualTo(ValueWrapper.of("15y", "Period.ofYears(15)")); + assertThat(values.get("periodZero")).isEqualTo(ValueWrapper.of(0, "Period.ZERO")); + assertThat(values.get("emptyArrayList")).isEqualTo(ValueWrapper.of("[]", "new ArrayList<>()")); + assertThat(values.get("emptyArrayListWithArg")) + .isEqualTo(ValueWrapper.unresolvable("new ArrayList<>(new HashSet<>())")); + assertThat(values.get("emptyFullyQualifiedLinkedListRedundantType")) + .isEqualTo(ValueWrapper.of("[]", "new java.util.LinkedList()")); + assertThat(values.get("emptyHashSet")).isEqualTo(ValueWrapper.of("[]", "new HashSet<>()")); + assertThat(values.get("emptyFullyQualifiedHashSetRedundantType")) + .isEqualTo(ValueWrapper.of("[]", "new java.util.HashSet()")); + assertThat(values.get("emptyTreeSet")).isEqualTo(ValueWrapper.of("[]", "new TreeSet<>()")); + assertThat(values.get("emptyHashMap")).isEqualTo(ValueWrapper.of("{}", "new HashMap<>()")); + assertThat(values.get("emptyHashMapWithArg")) + .isEqualTo(ValueWrapper.unresolvable("new HashMap<>(new HashMap<>())")); + assertThat(values.get("emptyFullyQualifiedTreeMapRedundantType")) + .isEqualTo(ValueWrapper.of("{}", "new java.util.TreeMap()")); } @SupportedAnnotationTypes({ "org.springframework.boot.configurationsample.ConfigurationProperties" }) @@ -115,7 +141,7 @@ private class TestProcessor extends AbstractProcessor { private FieldValuesParser processor; - private Map values = new HashMap<>(); + private Map values = new HashMap<>(); @Override public synchronized void init(ProcessingEnvironment env) { @@ -139,7 +165,7 @@ public boolean process(Set annotations, RoundEnvironment return false; } - Map getValues() { + Map getValues() { return this.values; } 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 ee8578c8a25f..f7891d8aab7a 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -20,6 +20,15 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.Period; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeSet; import org.springframework.boot.configurationsample.ConfigurationProperties; import org.springframework.util.MimeType; @@ -30,6 +39,7 @@ * * @author Phillip Webb * @author Stephane Nicoll + * @author Chris Bono */ @SuppressWarnings("unused") @ConfigurationProperties @@ -37,6 +47,8 @@ public class FieldValues { private static final String STRING_CONST = "c"; + private static final String STRING_CONST_NULL = null; + private static final boolean BOOLEAN_CONST = true; private static final Boolean BOOLEAN_OBJ_CONST = true; @@ -51,12 +63,20 @@ public class FieldValues { private static final String[] STRING_ARRAY_CONST = new String[] { "OK", "KO" }; + private final String STRING_FINAL = "d"; + private String string = "1"; private String stringNone; + private String stringNull = null; + private String stringConst = STRING_CONST; + private String stringConstNull = STRING_CONST_NULL; + + private String stringFinal = this.STRING_FINAL; + private boolean bool = true; private boolean boolNone; @@ -109,6 +129,8 @@ public class FieldValues { private Integer[] integerArray = new Integer[] { 42, 24 }; + private Object[] objectArrayBadEntry = new Object[] { STRING_CONST, new StringBuffer() }; + private UnknownElementType[] unknownArray = new UnknownElementType[] { new UnknownElementType() }; private Duration durationNone; @@ -151,4 +173,25 @@ public class FieldValues { private Period periodZero = Period.ZERO; + private List emptyArrayList = new ArrayList<>(); + + private List emptyArrayListWithArg = new ArrayList<>(new HashSet<>()); + + @SuppressWarnings("Convert2Diamond") + private List emptyFullyQualifiedLinkedListRedundantType = new java.util.LinkedList(); + + private Set emptyHashSet = new HashSet<>(); + + @SuppressWarnings("Convert2Diamond") + private Set emptyFullyQualifiedHashSetRedundantType = new java.util.HashSet(); + + private SortedSet emptyTreeSet = new TreeSet<>(); + + private Map emptyHashMap = new HashMap<>(); + + private Map emptyHashMapWithArg = new HashMap<>(new HashMap<>()); + + @SuppressWarnings("Convert2Diamond") + private SortedMap emptyFullyQualifiedTreeMapRedundantType = new java.util.TreeMap(); + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/simple/SimpleBadDefaultProperties.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/simple/SimpleBadDefaultProperties.java new file mode 100644 index 000000000000..5d45a415d2a7 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/simple/SimpleBadDefaultProperties.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2021 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationsample.simple; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.springframework.boot.configurationsample.ConfigurationProperties; + +/** + * Simple properties including some with unresolvable default values. + * + * @author Chris Bono + */ +@ConfigurationProperties(prefix = "simple.bad.default") +public class SimpleBadDefaultProperties { + + /** + * The name of this simple properties. + */ + private String theName = "boot"; + + /** + * A list of strings with unresolvable defaults. + */ + private List someList = new ArrayList<>(Arrays.asList("a", "b", "c")); + + public String getTheName() { + return this.theName; + } + + public void setTheName(String theName) { + this.theName = theName; + } + + public List getSomeList() { + return this.someList; + } + + public void setSomeList(List someList) { + this.someList = someList; + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/compiler/TestCompiler.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/compiler/TestCompiler.java index 532174dbdea6..325c491ee6e4 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/compiler/TestCompiler.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/compiler/TestCompiler.java @@ -67,17 +67,22 @@ public TestCompiler(JavaCompiler compiler, File outputLocation) throws IOExcepti public TestCompilationTask getTask(Collection sourceFiles) { Iterable javaFileObjects = this.fileManager.getJavaFileObjectsFromFiles(sourceFiles); - return getTask(javaFileObjects); + return getTask(javaFileObjects, null); } public TestCompilationTask getTask(Class... types) { Iterable javaFileObjects = getJavaFileObjects(types); - return getTask(javaFileObjects); + return getTask(javaFileObjects, null); } - private TestCompilationTask getTask(Iterable javaFileObjects) { + public TestCompilationTask getTaskWithOptions(Iterable options, Class... types) { + Iterable javaFileObjects = getJavaFileObjects(types); + return getTask(javaFileObjects, options); + } + + private TestCompilationTask getTask(Iterable javaFileObjects, Iterable options) { return new TestCompilationTask( - this.compiler.getTask(null, this.fileManager, null, null, null, javaFileObjects)); + this.compiler.getTask(null, this.fileManager, null, options, null, javaFileObjects)); } public File getOutputLocation() { diff --git a/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 57ba2fd3ee40..c2d92287ec07 100644 --- a/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -476,6 +476,11 @@ "type": "java.util.Properties", "description": "Vendor-specific XA properties." }, + { + "name": "spring.jta.atomikos.properties.default-max-wait-time-on-shutdown", + "type": "java.lang.Long", + "defaultValue": 9223372036854775807 + }, { "name": "spring.jta.bitronix.connectionfactory.acquire-increment", "type": "java.lang.Integer",