Merge branch '2.4.x' into 2.5.x
Closes gh-27060
This commit is contained in:
@@ -34,7 +34,9 @@ import org.springframework.beans.propertyeditors.FileEditor;
|
||||
import org.springframework.boot.convert.ApplicationConversionService;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.ConversionException;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.ConverterNotFoundException;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConditionalGenericConverter;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
@@ -98,17 +100,20 @@ final class BindConverter {
|
||||
}
|
||||
|
||||
private Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
for (int i = 0; i < this.delegates.size() - 1; i++) {
|
||||
ConversionException failure = null;
|
||||
for (ConversionService delegate : this.delegates) {
|
||||
try {
|
||||
ConversionService delegate = this.delegates.get(i);
|
||||
if (delegate.canConvert(sourceType, targetType)) {
|
||||
return delegate.convert(source, sourceType, targetType);
|
||||
}
|
||||
}
|
||||
catch (ConversionException ex) {
|
||||
if (failure == null && ex instanceof ConversionFailedException) {
|
||||
failure = ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.delegates.get(this.delegates.size() - 1).convert(source, sourceType, targetType);
|
||||
throw (failure != null) ? failure : new ConverterNotFoundException(sourceType, targetType);
|
||||
}
|
||||
|
||||
static BindConverter get(List<ConversionService> conversionServices,
|
||||
|
||||
@@ -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.
|
||||
@@ -38,6 +38,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class BindFailureAnalyzer extends AbstractFailureAnalyzer<BindException> {
|
||||
|
||||
@@ -68,16 +69,33 @@ class BindFailureAnalyzer extends AbstractFailureAnalyzer<BindException> {
|
||||
}
|
||||
|
||||
private String getMessage(BindException cause) {
|
||||
Throwable rootCause = getRootCause(cause.getCause());
|
||||
ConversionFailedException conversionFailure = findCause(cause, ConversionFailedException.class);
|
||||
if (conversionFailure != null) {
|
||||
return "failed to convert " + conversionFailure.getSourceType() + " to "
|
||||
String message = "failed to convert " + conversionFailure.getSourceType() + " to "
|
||||
+ conversionFailure.getTargetType();
|
||||
if (rootCause != null) {
|
||||
message += " (caused by " + getExceptionTypeAndMessage(rootCause) + ")";
|
||||
}
|
||||
return message;
|
||||
}
|
||||
Throwable failure = cause;
|
||||
while (failure.getCause() != null) {
|
||||
failure = failure.getCause();
|
||||
if (rootCause != null && StringUtils.hasText(rootCause.getMessage())) {
|
||||
return getExceptionTypeAndMessage(rootCause);
|
||||
}
|
||||
return (StringUtils.hasText(failure.getMessage()) ? failure.getMessage() : cause.getMessage());
|
||||
return getExceptionTypeAndMessage(cause);
|
||||
}
|
||||
|
||||
private Throwable getRootCause(Throwable cause) {
|
||||
Throwable rootCause = cause;
|
||||
while (rootCause != null && rootCause.getCause() != null) {
|
||||
rootCause = rootCause.getCause();
|
||||
}
|
||||
return rootCause;
|
||||
}
|
||||
|
||||
private String getExceptionTypeAndMessage(Throwable ex) {
|
||||
String message = ex.getMessage();
|
||||
return ex.getClass().getName() + (StringUtils.hasText(message) ? ": " + message : "");
|
||||
}
|
||||
|
||||
private FailureAnalysis getFailureAnalysis(Object description, BindException cause) {
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.beans.PropertyEditorRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.ConverterNotFoundException;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
@@ -178,6 +179,15 @@ class BindConverterTests {
|
||||
assertThat(result.getSeconds()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test // gh-27028
|
||||
void convertWhenConversionFailsThrowsConversionFailedExceptionRatherThanConverterNotFoundException() {
|
||||
BindConverter bindConverter = BindConverter.get(Collections.singletonList(new GenericConversionService()),
|
||||
null);
|
||||
assertThatExceptionOfType(ConversionFailedException.class)
|
||||
.isThrownBy(() -> bindConverter.convert("com.example.Missing", ResolvableType.forClass(Class.class)))
|
||||
.withRootCauseInstanceOf(ClassNotFoundException.class);
|
||||
}
|
||||
|
||||
private BindConverter getPropertyEditorOnlyBindConverter(
|
||||
Consumer<PropertyEditorRegistry> propertyEditorInitializer) {
|
||||
return BindConverter.get(Collections.singletonList(new ThrowingConversionService()), propertyEditorInitializer);
|
||||
|
||||
@@ -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.
|
||||
@@ -41,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class BindFailureAnalyzerTests {
|
||||
|
||||
@@ -76,7 +77,16 @@ class BindFailureAnalyzerTests {
|
||||
void bindExceptionWithNestedFailureShouldDisplayNestedMessage() {
|
||||
FailureAnalysis analysis = performAnalysis(NestedFailureConfiguration.class, "test.foo.value=hello");
|
||||
assertThat(analysis.getDescription()).contains(failure("test.foo.value", "hello",
|
||||
"\"test.foo.value\" from property source \"test\"", "This is a failure"));
|
||||
"\"test.foo.value\" from property source \"test\"", "java.lang.RuntimeException: This is a failure"));
|
||||
}
|
||||
|
||||
@Test // gh-27028
|
||||
void bindExceptionDueToClassNotFoundConvertionFailure() {
|
||||
FailureAnalysis analysis = performAnalysis(GenericFailureConfiguration.class,
|
||||
"test.foo.type=com.example.Missing");
|
||||
assertThat(analysis.getDescription()).contains(failure("test.foo.type", "com.example.Missing",
|
||||
"\"test.foo.type\" from property source \"test\"",
|
||||
"failed to convert java.lang.String to java.lang.Class<?> (caused by java.lang.ClassNotFoundException: com.example.Missing"));
|
||||
}
|
||||
|
||||
private static String failure(String property, String value, String origin, String reason) {
|
||||
@@ -178,6 +188,8 @@ class BindFailureAnalyzerTests {
|
||||
|
||||
private int value;
|
||||
|
||||
private Class<?> type;
|
||||
|
||||
int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
@@ -186,6 +198,14 @@ class BindFailureAnalyzerTests {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
Class<?> getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
void setType(Class<?> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties("test.foo")
|
||||
|
||||
Reference in New Issue
Block a user