Skip to content

Commit cdeecd4

Browse files
mp911dechristophstrobl
authored andcommitted
Throw UnsupportedOperationException when a projected value cannot be returned.
We now throw UnsupportedOperationException when a projected value cannot be returned because it cannot be brought into the target type, either via conversion or projection. This exception improves the error message by avoiding throwing IllegalArgumentException: Projection type must be an interface from the last branch that falls back into projections. Closes #2290. Original Pull Request: #2291
1 parent 5e702a8 commit cdeecd4

File tree

2 files changed

+26
-2
lines changed

2 files changed

+26
-2
lines changed

Diff for: src/main/java/org/springframework/data/projection/ProjectingMethodInterceptor.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,14 @@ protected Object potentiallyConvertResult(TypeInformation<?> type, @Nullable Obj
104104
return projectMapValues((Map<?, ?>) result, type);
105105
} else if (conversionRequiredAndPossible(result, type.getType())) {
106106
return conversionService.convert(result, type.getType());
107-
} else {
107+
} else if (ClassUtils.isAssignable(type.getType(), result.getClass())) {
108+
return result;
109+
} else if (type.getType().isInterface()) {
108110
return getProjection(result, type.getType());
111+
} else {
112+
throw new UnsupportedOperationException(String.format(
113+
"Cannot convert value '%s' of type '%s' to '%s' and cannot create a projection as the target type is not an interface",
114+
result, ClassUtils.getDescriptiveType(result), ClassUtils.getQualifiedName(type.getType())));
109115
}
110116
}
111117

@@ -155,7 +161,7 @@ private Map<Object, Object> projectMapValues(Map<?, ?> sources, TypeInformation<
155161
}
156162

157163
@Nullable
158-
private Object getProjection(Object result, Class<?> returnType) {
164+
private Object getProjection(@Nullable Object result, Class<?> returnType) {
159165
return result == null || ClassUtils.isAssignable(returnType, result.getClass()) ? result
160166
: factory.createProjection(returnType, result);
161167
}

Diff for: src/test/java/org/springframework/data/projection/ProjectingMethodInterceptorUnitTests.java

+18
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static org.mockito.ArgumentMatchers.*;
2020
import static org.mockito.Mockito.*;
2121

22+
import java.math.BigInteger;
2223
import java.util.Collection;
2324
import java.util.Collections;
2425
import java.util.EnumSet;
@@ -95,6 +96,21 @@ void considersPrimitivesAsWrappers() throws Throwable {
9596
verify(factory, times(0)).createProjection((Class<?>) any(), any());
9697
}
9798

99+
@Test // #2290
100+
void failsForNonConvertibleTypes() throws Throwable {
101+
102+
MethodInterceptor methodInterceptor = new ProjectingMethodInterceptor(factory, interceptor, conversionService);
103+
104+
when(invocation.getMethod()).thenReturn(Helper.class.getMethod("getBoolean"));
105+
when(interceptor.invoke(invocation)).thenReturn(BigInteger.valueOf(1));
106+
107+
assertThatThrownBy(() -> methodInterceptor.invoke(invocation)) //
108+
.isInstanceOf(UnsupportedOperationException.class) //
109+
.hasMessageContaining("'1'") //
110+
.hasMessageContaining("BigInteger") //
111+
.hasMessageContaining("boolean");
112+
}
113+
98114
@Test // DATAREST-394, DATAREST-408
99115
@SuppressWarnings("unchecked")
100116
void appliesProjectionToNonEmptySets() throws Throwable {
@@ -213,6 +229,8 @@ interface Helper {
213229

214230
long getPrimitive();
215231

232+
boolean getBoolean();
233+
216234
Collection<HelperProjection> getHelperCollection();
217235

218236
List<HelperProjection> getHelperList();

0 commit comments

Comments
 (0)