Polish InvalidConfigurationPropertyValueException
This commit is contained in:
parent
937a62e0b8
commit
a62a27e686
|
|
@ -23,8 +23,7 @@ package org.springframework.boot.context.properties.source;
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class InvalidConfigurationPropertyValueException
|
public class InvalidConfigurationPropertyValueException extends RuntimeException {
|
||||||
extends RuntimeException {
|
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
|
@ -34,8 +33,7 @@ public class InvalidConfigurationPropertyValueException
|
||||||
|
|
||||||
public InvalidConfigurationPropertyValueException(String name, Object value,
|
public InvalidConfigurationPropertyValueException(String name, Object value,
|
||||||
String reason) {
|
String reason) {
|
||||||
super(String.format("Property %s with value '%s' is invalid: %s", name,
|
super("Property " + name + " with value '" + value + "' is invalid: " + reason);
|
||||||
value, reason));
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.reason = reason;
|
this.reason = reason;
|
||||||
|
|
|
||||||
|
|
@ -16,16 +16,18 @@
|
||||||
|
|
||||||
package org.springframework.boot.diagnostics.analyzer;
|
package org.springframework.boot.diagnostics.analyzer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
||||||
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
|
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
|
||||||
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
|
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
|
||||||
import org.springframework.boot.diagnostics.FailureAnalysis;
|
import org.springframework.boot.diagnostics.FailureAnalysis;
|
||||||
import org.springframework.boot.diagnostics.FailureAnalyzer;
|
import org.springframework.boot.diagnostics.FailureAnalyzer;
|
||||||
|
import org.springframework.boot.origin.Origin;
|
||||||
import org.springframework.boot.origin.OriginLookup;
|
import org.springframework.boot.origin.OriginLookup;
|
||||||
import org.springframework.context.EnvironmentAware;
|
import org.springframework.context.EnvironmentAware;
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
|
|
@ -36,7 +38,7 @@ import org.springframework.util.StringUtils;
|
||||||
/**
|
/**
|
||||||
* A {@link FailureAnalyzer} that performs analysis of failures caused by a
|
* A {@link FailureAnalyzer} that performs analysis of failures caused by a
|
||||||
* {@link InvalidConfigurationPropertyValueException}.
|
* {@link InvalidConfigurationPropertyValueException}.
|
||||||
|
*
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
*/
|
*/
|
||||||
class InvalidConfigurationPropertyValueFailureAnalyzer
|
class InvalidConfigurationPropertyValueFailureAnalyzer
|
||||||
|
|
@ -53,94 +55,114 @@ class InvalidConfigurationPropertyValueFailureAnalyzer
|
||||||
@Override
|
@Override
|
||||||
protected FailureAnalysis analyze(Throwable rootFailure,
|
protected FailureAnalysis analyze(Throwable rootFailure,
|
||||||
InvalidConfigurationPropertyValueException cause) {
|
InvalidConfigurationPropertyValueException cause) {
|
||||||
List<PropertyValueDescriptor> descriptors = getPropertySourceDescriptors(cause.getName());
|
List<Descriptor> descriptors = getDescriptors(cause.getName());
|
||||||
if (descriptors.isEmpty()) {
|
if (descriptors.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
PropertyValueDescriptor main = descriptors.get(0);
|
StringBuilder description = new StringBuilder();
|
||||||
StringBuilder message = new StringBuilder();
|
appendDetails(description, cause, descriptors);
|
||||||
message.append(String.format("The value '%s'", main.value));
|
appendReason(description, cause);
|
||||||
if (main.origin != null) {
|
appendAdditionalProperties(description, descriptors);
|
||||||
message.append(String.format(" from origin '%s'", main.origin));
|
return new FailureAnalysis(description.toString(), getAction(cause), cause);
|
||||||
}
|
}
|
||||||
message.append(String.format(" of configuration property '%s' was invalid. ",
|
|
||||||
cause.getName()));
|
private List<Descriptor> getDescriptors(String propertyName) {
|
||||||
|
return getPropertySources()
|
||||||
|
.filter((source) -> source.containsProperty(propertyName))
|
||||||
|
.map((source) -> Descriptor.get(source, propertyName))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<PropertySource<?>> getPropertySources() {
|
||||||
|
Iterable<PropertySource<?>> sources = (this.environment == null
|
||||||
|
? Collections.emptyList() : this.environment.getPropertySources());
|
||||||
|
return StreamSupport.stream(sources.spliterator(), false)
|
||||||
|
.filter((source) -> !ConfigurationPropertySources
|
||||||
|
.isAttachedConfigurationPropertySource(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendDetails(StringBuilder message,
|
||||||
|
InvalidConfigurationPropertyValueException cause,
|
||||||
|
List<Descriptor> descriptors) {
|
||||||
|
Descriptor mainDescriptor = descriptors.get(0);
|
||||||
|
message.append("Invalid value '" + mainDescriptor.getValue()
|
||||||
|
+ "' for configuration property '" + cause.getName() + "'");
|
||||||
|
mainDescriptor.appendOrigin(message);
|
||||||
|
message.append(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendReason(StringBuilder message,
|
||||||
|
InvalidConfigurationPropertyValueException cause) {
|
||||||
if (StringUtils.hasText(cause.getReason())) {
|
if (StringUtils.hasText(cause.getReason())) {
|
||||||
message.append(String.format(
|
message.append(" Validation failed for the following reason\n\n");
|
||||||
"Validation failed for the following reason:%n%n"));
|
|
||||||
message.append(cause.getReason());
|
message.append(cause.getReason());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
message.append("No reason was provided.");
|
message.append(" No reason was provided.");
|
||||||
}
|
}
|
||||||
List<PropertyValueDescriptor> others = descriptors.subList(1, descriptors.size());
|
}
|
||||||
|
|
||||||
|
private void appendAdditionalProperties(StringBuilder message,
|
||||||
|
List<Descriptor> descriptors) {
|
||||||
|
List<Descriptor> others = descriptors.subList(1, descriptors.size());
|
||||||
if (!others.isEmpty()) {
|
if (!others.isEmpty()) {
|
||||||
message.append(String.format(
|
message.append(String.format(
|
||||||
"%n%nAdditionally, this property is also set in the following "
|
"%n%nAdditionally, this property is also set in the following "
|
||||||
+ "property %s:%n%n",
|
+ "property %s:%n%n",
|
||||||
others.size() > 1 ? "sources" : "source"));
|
others.size() > 1 ? "sources" : "source"));
|
||||||
for (PropertyValueDescriptor other : others) {
|
for (Descriptor other : others) {
|
||||||
message.append(String.format("\t- %s: %s%n", other.propertySource,
|
message.append("\t- In '" + other.getPropertySource() + "'");
|
||||||
other.value));
|
message.append(" with the value '" + other.getValue() + "'");
|
||||||
|
other.appendOrigin(message);
|
||||||
|
message.append(".\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getAction(InvalidConfigurationPropertyValueException cause) {
|
||||||
StringBuilder action = new StringBuilder();
|
StringBuilder action = new StringBuilder();
|
||||||
action.append("Review the value of the property");
|
action.append("Review the value of the property");
|
||||||
if (cause.getReason() != null) {
|
if (cause.getReason() != null) {
|
||||||
action.append(" with the provided reason");
|
action.append(" with the provided reason");
|
||||||
}
|
}
|
||||||
action.append(".");
|
action.append(".");
|
||||||
return new FailureAnalysis(message.toString(), action.toString(), cause);
|
return action.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<PropertyValueDescriptor> getPropertySourceDescriptors(
|
private static final class Descriptor {
|
||||||
String propertyName) {
|
|
||||||
List<PropertyValueDescriptor> propertySources = new ArrayList<>();
|
|
||||||
getPropertySourcesAsMap()
|
|
||||||
.forEach((sourceName, source) -> {
|
|
||||||
if (source.containsProperty(propertyName)) {
|
|
||||||
propertySources.add(describeValueOf(propertyName, source));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return propertySources;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, PropertySource<?>> getPropertySourcesAsMap() {
|
|
||||||
Map<String, PropertySource<?>> map = new LinkedHashMap<>();
|
|
||||||
if (this.environment != null) {
|
|
||||||
for (PropertySource<?> source : this.environment.getPropertySources()) {
|
|
||||||
if (!ConfigurationPropertySources
|
|
||||||
.isAttachedConfigurationPropertySource(source)) {
|
|
||||||
map.put(source.getName(), source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PropertyValueDescriptor describeValueOf(String name,
|
|
||||||
PropertySource<?> source) {
|
|
||||||
Object value = source.getProperty(name);
|
|
||||||
String origin = (source instanceof OriginLookup)
|
|
||||||
? ((OriginLookup<Object>) source).getOrigin(name).toString() : null;
|
|
||||||
return new PropertyValueDescriptor(source.getName(), value, origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PropertyValueDescriptor {
|
|
||||||
|
|
||||||
private final String propertySource;
|
private final String propertySource;
|
||||||
|
|
||||||
private final Object value;
|
private final Object value;
|
||||||
|
|
||||||
private final String origin;
|
private final Origin origin;
|
||||||
|
|
||||||
PropertyValueDescriptor(String propertySource,
|
private Descriptor(String propertySource, Object value, Origin origin) {
|
||||||
Object value, String origin) {
|
|
||||||
this.propertySource = propertySource;
|
this.propertySource = propertySource;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.origin = origin;
|
this.origin = origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPropertySource() {
|
||||||
|
return this.propertySource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendOrigin(StringBuilder message) {
|
||||||
|
if (this.origin != null) {
|
||||||
|
message.append(" (originating from '" + this.origin + "')");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Descriptor get(PropertySource<?> source, String propertyName) {
|
||||||
|
Object value = source.getProperty(propertyName);
|
||||||
|
Origin origin = OriginLookup.getOrigin(source, propertyName);
|
||||||
|
return new Descriptor(source.getName(), value, origin);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,8 @@ public class InvalidConfigurationPropertyValueFailureAnalyzerTests {
|
||||||
public void analysisWithKnownProperty() {
|
public void analysisWithKnownProperty() {
|
||||||
MapPropertySource source = new MapPropertySource("test",
|
MapPropertySource source = new MapPropertySource("test",
|
||||||
Collections.singletonMap("test.property", "invalid"));
|
Collections.singletonMap("test.property", "invalid"));
|
||||||
this.environment.getPropertySources().addFirst(new OriginCapablePropertySource(source));
|
this.environment.getPropertySources()
|
||||||
|
.addFirst(OriginCapablePropertySource.get(source));
|
||||||
InvalidConfigurationPropertyValueException failure = new InvalidConfigurationPropertyValueException(
|
InvalidConfigurationPropertyValueException failure = new InvalidConfigurationPropertyValueException(
|
||||||
"test.property", "invalid", "this is not valid");
|
"test.property", "invalid", "this is not valid");
|
||||||
FailureAnalysis analysis = performAnalysis(failure);
|
FailureAnalysis analysis = performAnalysis(failure);
|
||||||
|
|
@ -68,13 +69,13 @@ public class InvalidConfigurationPropertyValueFailureAnalyzerTests {
|
||||||
public void analysisWithKnownPropertyAndNoReason() {
|
public void analysisWithKnownPropertyAndNoReason() {
|
||||||
MapPropertySource source = new MapPropertySource("test",
|
MapPropertySource source = new MapPropertySource("test",
|
||||||
Collections.singletonMap("test.property", "invalid"));
|
Collections.singletonMap("test.property", "invalid"));
|
||||||
this.environment.getPropertySources().addFirst(new OriginCapablePropertySource(source));
|
this.environment.getPropertySources()
|
||||||
|
.addFirst(OriginCapablePropertySource.get(source));
|
||||||
InvalidConfigurationPropertyValueException failure = new InvalidConfigurationPropertyValueException(
|
InvalidConfigurationPropertyValueException failure = new InvalidConfigurationPropertyValueException(
|
||||||
"test.property", "invalid", null);
|
"test.property", "invalid", null);
|
||||||
FailureAnalysis analysis = performAnalysis(failure);
|
FailureAnalysis analysis = performAnalysis(failure);
|
||||||
assertCommonParts(failure, analysis);
|
assertCommonParts(failure, analysis);
|
||||||
assertThat(analysis.getDescription())
|
assertThat(analysis.getDescription()).contains("No reason was provided.")
|
||||||
.contains("No reason was provided.")
|
|
||||||
.doesNotContain("Additionally, this property is also set");
|
.doesNotContain("Additionally, this property is also set");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,17 +87,20 @@ public class InvalidConfigurationPropertyValueFailureAnalyzerTests {
|
||||||
Collections.singletonMap("test.property", "valid"));
|
Collections.singletonMap("test.property", "valid"));
|
||||||
MapPropertySource another = new MapPropertySource("another",
|
MapPropertySource another = new MapPropertySource("another",
|
||||||
Collections.singletonMap("test.property", "test"));
|
Collections.singletonMap("test.property", "test"));
|
||||||
this.environment.getPropertySources().addFirst(new OriginCapablePropertySource(source));
|
this.environment.getPropertySources()
|
||||||
|
.addFirst(OriginCapablePropertySource.get(source));
|
||||||
this.environment.getPropertySources().addLast(additional);
|
this.environment.getPropertySources().addLast(additional);
|
||||||
this.environment.getPropertySources().addLast(another);
|
this.environment.getPropertySources()
|
||||||
|
.addLast(OriginCapablePropertySource.get(another));
|
||||||
InvalidConfigurationPropertyValueException failure = new InvalidConfigurationPropertyValueException(
|
InvalidConfigurationPropertyValueException failure = new InvalidConfigurationPropertyValueException(
|
||||||
"test.property", "invalid", "this is not valid");
|
"test.property", "invalid", "this is not valid");
|
||||||
FailureAnalysis analysis = performAnalysis(failure);
|
FailureAnalysis analysis = performAnalysis(failure);
|
||||||
assertCommonParts(failure, analysis);
|
assertCommonParts(failure, analysis);
|
||||||
assertThat(analysis.getDescription())
|
assertThat(analysis.getDescription())
|
||||||
.contains("Additionally, this property is also set in the following " +
|
.contains("Additionally, this property is also set in the following "
|
||||||
"property sources:")
|
+ "property sources:")
|
||||||
.contains("additional: valid").contains("another: test");
|
.contains("In 'additional' with the value 'valid'").contains(
|
||||||
|
"In 'another' with the value 'test' (originating from 'TestOrigin test.property')");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -115,7 +119,6 @@ public class InvalidConfigurationPropertyValueFailureAnalyzerTests {
|
||||||
assertThat(analysis.getCause()).isSameAs(failure);
|
assertThat(analysis.getCause()).isSameAs(failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private FailureAnalysis performAnalysis(
|
private FailureAnalysis performAnalysis(
|
||||||
InvalidConfigurationPropertyValueException failure) {
|
InvalidConfigurationPropertyValueException failure) {
|
||||||
InvalidConfigurationPropertyValueFailureAnalyzer analyzer = new InvalidConfigurationPropertyValueFailureAnalyzer();
|
InvalidConfigurationPropertyValueFailureAnalyzer analyzer = new InvalidConfigurationPropertyValueFailureAnalyzer();
|
||||||
|
|
@ -127,8 +130,8 @@ public class InvalidConfigurationPropertyValueFailureAnalyzerTests {
|
||||||
return analysis;
|
return analysis;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class OriginCapablePropertySource<T>
|
static class OriginCapablePropertySource<T> extends EnumerablePropertySource<T>
|
||||||
extends EnumerablePropertySource<T> implements OriginLookup<String> {
|
implements OriginLookup<String> {
|
||||||
|
|
||||||
private final EnumerablePropertySource<T> propertySource;
|
private final EnumerablePropertySource<T> propertySource;
|
||||||
|
|
||||||
|
|
@ -159,6 +162,11 @@ public class InvalidConfigurationPropertyValueFailureAnalyzerTests {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static <T> OriginCapablePropertySource<T> get(
|
||||||
|
EnumerablePropertySource<T> propertySource) {
|
||||||
|
return new OriginCapablePropertySource<>(propertySource);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue