Skip to content

Commit 84fab65

Browse files
committed
Merge branch '2.4.x' into 2.5.x
Closes gh-27060
2 parents e9d3e0d + ea62967 commit 84fab65

File tree

4 files changed

+64
-11
lines changed

4 files changed

+64
-11
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/BindConverter.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
import org.springframework.boot.convert.ApplicationConversionService;
3535
import org.springframework.core.ResolvableType;
3636
import org.springframework.core.convert.ConversionException;
37+
import org.springframework.core.convert.ConversionFailedException;
3738
import org.springframework.core.convert.ConversionService;
39+
import org.springframework.core.convert.ConverterNotFoundException;
3840
import org.springframework.core.convert.TypeDescriptor;
3941
import org.springframework.core.convert.converter.ConditionalGenericConverter;
4042
import org.springframework.core.convert.support.GenericConversionService;
@@ -98,17 +100,20 @@ <T> T convert(Object source, ResolvableType targetType, Annotation... targetAnno
98100
}
99101

100102
private Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
101-
for (int i = 0; i < this.delegates.size() - 1; i++) {
103+
ConversionException failure = null;
104+
for (ConversionService delegate : this.delegates) {
102105
try {
103-
ConversionService delegate = this.delegates.get(i);
104106
if (delegate.canConvert(sourceType, targetType)) {
105107
return delegate.convert(source, sourceType, targetType);
106108
}
107109
}
108110
catch (ConversionException ex) {
111+
if (failure == null && ex instanceof ConversionFailedException) {
112+
failure = ex;
113+
}
109114
}
110115
}
111-
return this.delegates.get(this.delegates.size() - 1).convert(source, sourceType, targetType);
116+
throw (failure != null) ? failure : new ConverterNotFoundException(sourceType, targetType);
112117
}
113118

114119
static BindConverter get(List<ConversionService> conversionServices,

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/BindFailureAnalyzer.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -38,6 +38,7 @@
3838
*
3939
* @author Andy Wilkinson
4040
* @author Madhura Bhave
41+
* @author Phillip Webb
4142
*/
4243
class BindFailureAnalyzer extends AbstractFailureAnalyzer<BindException> {
4344

@@ -68,16 +69,33 @@ private void buildDescription(StringBuilder description, ConfigurationProperty p
6869
}
6970

7071
private String getMessage(BindException cause) {
72+
Throwable rootCause = getRootCause(cause.getCause());
7173
ConversionFailedException conversionFailure = findCause(cause, ConversionFailedException.class);
7274
if (conversionFailure != null) {
73-
return "failed to convert " + conversionFailure.getSourceType() + " to "
75+
String message = "failed to convert " + conversionFailure.getSourceType() + " to "
7476
+ conversionFailure.getTargetType();
77+
if (rootCause != null) {
78+
message += " (caused by " + getExceptionTypeAndMessage(rootCause) + ")";
79+
}
80+
return message;
81+
}
82+
if (rootCause != null && StringUtils.hasText(rootCause.getMessage())) {
83+
return getExceptionTypeAndMessage(rootCause);
7584
}
76-
Throwable failure = cause;
77-
while (failure.getCause() != null) {
78-
failure = failure.getCause();
85+
return getExceptionTypeAndMessage(cause);
86+
}
87+
88+
private Throwable getRootCause(Throwable cause) {
89+
Throwable rootCause = cause;
90+
while (rootCause != null && rootCause.getCause() != null) {
91+
rootCause = rootCause.getCause();
7992
}
80-
return (StringUtils.hasText(failure.getMessage()) ? failure.getMessage() : cause.getMessage());
93+
return rootCause;
94+
}
95+
96+
private String getExceptionTypeAndMessage(Throwable ex) {
97+
String message = ex.getMessage();
98+
return ex.getClass().getName() + (StringUtils.hasText(message) ? ": " + message : "");
8199
}
82100

83101
private FailureAnalysis getFailureAnalysis(Object description, BindException cause) {

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/BindConverterTests.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import org.springframework.beans.PropertyEditorRegistry;
3232
import org.springframework.core.ResolvableType;
33+
import org.springframework.core.convert.ConversionFailedException;
3334
import org.springframework.core.convert.ConversionService;
3435
import org.springframework.core.convert.ConverterNotFoundException;
3536
import org.springframework.core.convert.TypeDescriptor;
@@ -178,6 +179,15 @@ void fallsBackToApplicationConversionService() {
178179
assertThat(result.getSeconds()).isEqualTo(10);
179180
}
180181

182+
@Test // gh-27028
183+
void convertWhenConversionFailsThrowsConversionFailedExceptionRatherThanConverterNotFoundException() {
184+
BindConverter bindConverter = BindConverter.get(Collections.singletonList(new GenericConversionService()),
185+
null);
186+
assertThatExceptionOfType(ConversionFailedException.class)
187+
.isThrownBy(() -> bindConverter.convert("com.example.Missing", ResolvableType.forClass(Class.class)))
188+
.withRootCauseInstanceOf(ClassNotFoundException.class);
189+
}
190+
181191
private BindConverter getPropertyEditorOnlyBindConverter(
182192
Consumer<PropertyEditorRegistry> propertyEditorInitializer) {
183193
return BindConverter.get(Collections.singletonList(new ThrowingConversionService()), propertyEditorInitializer);

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/BindFailureAnalyzerTests.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@
4141
*
4242
* @author Andy Wilkinson
4343
* @author Madhura Bhave
44+
* @author Phillip Webb
4445
*/
4546
class BindFailureAnalyzerTests {
4647

@@ -76,7 +77,16 @@ void bindExceptionForUnknownValueInEnumListsValidValuesInAction() {
7677
void bindExceptionWithNestedFailureShouldDisplayNestedMessage() {
7778
FailureAnalysis analysis = performAnalysis(NestedFailureConfiguration.class, "test.foo.value=hello");
7879
assertThat(analysis.getDescription()).contains(failure("test.foo.value", "hello",
79-
"\"test.foo.value\" from property source \"test\"", "This is a failure"));
80+
"\"test.foo.value\" from property source \"test\"", "java.lang.RuntimeException: This is a failure"));
81+
}
82+
83+
@Test // gh-27028
84+
void bindExceptionDueToClassNotFoundConvertionFailure() {
85+
FailureAnalysis analysis = performAnalysis(GenericFailureConfiguration.class,
86+
"test.foo.type=com.example.Missing");
87+
assertThat(analysis.getDescription()).contains(failure("test.foo.type", "com.example.Missing",
88+
"\"test.foo.type\" from property source \"test\"",
89+
"failed to convert java.lang.String to java.lang.Class<?> (caused by java.lang.ClassNotFoundException: com.example.Missing"));
8090
}
8191

8292
private static String failure(String property, String value, String origin, String reason) {
@@ -178,6 +188,8 @@ static class GenericFailureProperties {
178188

179189
private int value;
180190

191+
private Class<?> type;
192+
181193
int getValue() {
182194
return this.value;
183195
}
@@ -186,6 +198,14 @@ void setValue(int value) {
186198
this.value = value;
187199
}
188200

201+
Class<?> getType() {
202+
return this.type;
203+
}
204+
205+
void setType(Class<?> type) {
206+
this.type = type;
207+
}
208+
189209
}
190210

191211
@ConfigurationProperties("test.foo")

0 commit comments

Comments
 (0)