Polish InvalidConfigurationPropertyValueException

pull/11498/merge
Phillip Webb 7 years ago
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(
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() { private static final class Descriptor {
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…
Cancel
Save