Skip to content

Commit c1aad4e

Browse files
committed
Polishing.
1 parent d136577 commit c1aad4e

File tree

9 files changed

+106
-49
lines changed

9 files changed

+106
-49
lines changed

src/main/java/org/springframework/data/core/TypeParsingException.java renamed to src/main/java/org/springframework/data/core/LambdaIntrospectionException.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,36 @@
1616
package org.springframework.data.core;
1717

1818
import org.jspecify.annotations.Nullable;
19-
import org.springframework.core.NestedRuntimeException;
2019

2120
/**
22-
* {@link RuntimeException} for errors during parsing or introspection of types.
21+
* {@link RuntimeException} for errors during {@link java.lang.invoke.SerializedLambda} parsing or introspection of
22+
* types.
2323
* <p>
2424
* Typically, thrown when a lambda or method reference is not a serializable lambda, or violates supported patterns
2525
* (e.g. constructor references, multi-step access).
2626
*
2727
* @author Christoph Strobl
2828
* @since 4.1
2929
*/
30-
public class TypeParsingException extends NestedRuntimeException { // TypeIntrospectionException ???
30+
public class LambdaIntrospectionException extends PropertyResolutionException {
3131

32-
public TypeParsingException(@Nullable String msg) {
32+
/**
33+
* Construct a {@code LambdaIntrospectionException} with the specified detail message.
34+
*
35+
* @param msg the detail message.
36+
*/
37+
public LambdaIntrospectionException(@Nullable String msg) {
3338
super(msg);
3439
}
3540

36-
public TypeParsingException(@Nullable String msg, @Nullable Throwable cause) {
41+
/**
42+
* Construct a {@code LambdaIntrospectionException} with the specified detail message and nested exception.
43+
*
44+
* @param msg the detail message.
45+
* @param cause the nested exception.
46+
*/
47+
public LambdaIntrospectionException(@Nullable String msg, @Nullable Throwable cause) {
3748
super(msg, cause);
3849
}
50+
3951
}

src/main/java/org/springframework/data/core/PropertyPathUtil.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@
2222

2323
import org.jspecify.annotations.Nullable;
2424

25-
import org.springframework.dao.InvalidDataAccessApiUsageException;
2625
import org.springframework.util.Assert;
27-
import org.springframework.util.ObjectUtils;
2826
import org.springframework.util.ReflectionUtils;
2927

3028
/**
@@ -63,7 +61,7 @@ private record SerializableWrapper(Serializable serializable) implements Propert
6361
Method method = ReflectionUtils.findMethod(serializable.getClass(), "writeReplace");
6462

6563
if (method == null) {
66-
throw new InvalidDataAccessApiUsageException(
64+
throw new LambdaIntrospectionException(
6765
"Cannot find writeReplace method on " + serializable.getClass().getName());
6866
}
6967

src/main/java/org/springframework/data/core/PropertyReference.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,8 @@
4848
* <p>
4949
* Implement {@code PropertyReference} using method references (strongly recommended) or lambdas that directly access a
5050
* property getter. Constructor references, method calls with parameters, and complex expressions are not supported and
51-
* result in {@link org.springframework.dao.InvalidDataAccessApiUsageException}. Unlike method references, introspection
52-
* of lambda expressions requires bytecode analysis of the declaration site classes and thus depends on their
53-
* availability at runtime.
51+
* result in {@link PropertyResolutionException}. Unlike method references, introspection of lambda expressions requires
52+
* bytecode analysis of the declaration site classes and thus depends on their availability at runtime.
5453
*
5554
* @param <T> the owning type of this property.
5655
* @param <P> the property value type.

src/main/java/org/springframework/data/core/PropertyReferenceException.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
* @author Christoph Strobl
3737
* @author John Blum
3838
*/
39-
public class PropertyReferenceException extends RuntimeException {
39+
public class PropertyReferenceException extends PropertyResolutionException {
4040

4141
private static final @Serial long serialVersionUID = -5254424051438976570L;
4242

@@ -58,6 +58,8 @@ public class PropertyReferenceException extends RuntimeException {
5858
public PropertyReferenceException(String propertyName, TypeInformation<?> type,
5959
List<? extends PropertyPath> alreadyResolvedPath) {
6060

61+
super(null);
62+
6163
Assert.hasText(propertyName, "Property name must not be null");
6264
Assert.notNull(type, "Type must not be null");
6365
Assert.notNull(alreadyResolvedPath, "Already resolved paths must not be null");
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2026-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.core;
17+
18+
import org.jspecify.annotations.Nullable;
19+
import org.springframework.core.NestedRuntimeException;
20+
21+
/**
22+
* Exception indicating errors during property reference or property path resolution. Thrown when a property cannot be
23+
* resolved.
24+
*
25+
* @author Mark Paluch
26+
* @since 4.1
27+
*/
28+
public class PropertyResolutionException extends NestedRuntimeException {
29+
30+
/**
31+
* Construct a {@code PropertyResolutionException} with the specified detail message.
32+
*
33+
* @param msg the detail message.
34+
*/
35+
public PropertyResolutionException(@Nullable String msg) {
36+
super(msg);
37+
}
38+
39+
/**
40+
* Construct a {@code PropertyResolutionException} with the specified detail message and nested exception.
41+
*
42+
* @param msg the detail message.
43+
* @param cause the nested exception.
44+
*/
45+
public PropertyResolutionException(@Nullable String msg, @Nullable Throwable cause) {
46+
super(msg, cause);
47+
}
48+
49+
}

src/main/java/org/springframework/data/core/SerializableLambdaReader.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ private static boolean isEnabled(String property, boolean defaultValue) {
135135
*
136136
* @param lambdaObject the actual lambda object, must be {@link java.io.Serializable}.
137137
* @return the member reference.
138-
* @throws TypeParsingException if the lambda object does not contain a valid property reference or hits
139-
* any of the mentioned limitations.
138+
* @throws LambdaIntrospectionException if the lambda object does not contain a valid property reference or hits any
139+
* of the mentioned limitations.
140140
*/
141141
public MemberDescriptor read(Object lambdaObject) {
142142

@@ -170,10 +170,10 @@ public MemberDescriptor read(Object lambdaObject) {
170170
return getMemberDescriptor(lambdaObject, lambda);
171171
}
172172
} catch (ReflectiveOperationException | IOException e) {
173-
throw new TypeParsingException("Cannot extract method or field", e);
173+
throw new LambdaIntrospectionException("Cannot extract method or field", e);
174174
}
175175

176-
throw new TypeParsingException("Cannot extract method or field from: " + lambdaObject
176+
throw new LambdaIntrospectionException("Cannot extract method or field from: " + lambdaObject
177177
+ ". The given value is not a lambda or method reference.");
178178
}
179179

@@ -182,7 +182,7 @@ private void assertNotConstructor(SerializedLambda lambda) {
182182
if (lambda.getImplMethodKind() == MethodHandleInfo.REF_newInvokeSpecial
183183
|| lambda.getImplMethodKind() == MethodHandleInfo.REF_invokeSpecial) {
184184

185-
TypeParsingException e = new TypeParsingException(
185+
LambdaIntrospectionException e = new LambdaIntrospectionException(
186186
"Method reference must not be a constructor call");
187187

188188
if (filterStackTrace) {
@@ -222,7 +222,7 @@ private static SerializedLambda serialize(Object lambda) {
222222
method.setAccessible(true);
223223
return (SerializedLambda) method.invoke(lambda);
224224
} catch (ReflectiveOperationException e) {
225-
throw new TypeParsingException(
225+
throw new LambdaIntrospectionException(
226226
"Not a lambda: " + (lambda instanceof Enum<?> ? lambda.getClass().getName() + "#" + lambda : lambda), e);
227227
}
228228
}
@@ -296,7 +296,7 @@ public static MemberDescriptor read(Object captured, Object lambda) {
296296
return KPropertyPathDescriptor.create(propRef);
297297
}
298298

299-
throw new TypeParsingException("Cannot extract MemberDescriptor from: " + lambda);
299+
throw new LambdaIntrospectionException("Cannot extract MemberDescriptor from: " + lambda);
300300
}
301301

302302
}
@@ -466,7 +466,7 @@ public MemberDescriptor resolve(SerializedLambda lambda) {
466466
if (errors.isEmpty()) {
467467

468468
if (memberDescriptors.isEmpty()) {
469-
throw new TypeParsingException("There is no method or field access");
469+
throw new LambdaIntrospectionException("There is no method or field access");
470470
}
471471

472472
return memberDescriptors.get(memberDescriptors.size() - 1);
@@ -477,7 +477,7 @@ public MemberDescriptor resolve(SerializedLambda lambda) {
477477

478478
String methodName = getDeclaringMethodName(lambda);
479479

480-
TypeParsingException e = new TypeParsingException(
480+
LambdaIntrospectionException e = new LambdaIntrospectionException(
481481
"Cannot resolve property path%n%nError%s:%n".formatted(errors.size() > 1 ? "s" : "") + errors.stream()
482482
.map(ReadingError::message).map(LambdaMethodVisitor::formatMessage).collect(Collectors.joining()));
483483

@@ -497,7 +497,7 @@ public MemberDescriptor resolve(SerializedLambda lambda) {
497497
throw e;
498498
}
499499

500-
throw new TypeParsingException("Error resolving " + errors);
500+
throw new LambdaIntrospectionException("Error resolving " + errors);
501501
}
502502

503503
private static String formatMessage(String args) {

src/main/java/org/springframework/data/core/TypedPropertyPath.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,8 @@
5555
* <p>
5656
* Implement {@code TypedPropertyPath} using method references (strongly recommended)s that directly access a property
5757
* getter. Constructor references, method calls with parameters, and complex expressions are not supported and result in
58-
* {@link org.springframework.dao.InvalidDataAccessApiUsageException}. Unlike method references, introspection of lambda
59-
* expressions requires bytecode analysis of the declaration site classes and thus depends on their availability at
60-
* runtime.
58+
* {@link PropertyResolutionException}. Unlike method references, introspection of lambda expressions requires bytecode
59+
* analysis of the declaration site classes and thus depends on their availability at runtime.
6160
*
6261
* @param <T> the owning type of this path segment; the root type for composed paths.
6362
* @param <P> the property value type at this path segment.

src/test/java/org/springframework/data/core/PropertyReferenceUnitTests.java

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import org.jspecify.annotations.Nullable;
2323
import org.junit.jupiter.api.Nested;
2424
import org.junit.jupiter.api.Test;
25-
import org.springframework.dao.InvalidDataAccessApiUsageException;
2625

2726
/**
2827
* Unit tests for {@link PropertyReference}.
@@ -101,7 +100,7 @@ void resolvesPrivateMethodLambda() {
101100
@Test // GH-3400
102101
void switchingOwningTypeFails() {
103102

104-
assertThatExceptionOfType(TypeParsingException.class)
103+
assertThatExceptionOfType(LambdaIntrospectionException.class)
105104
.isThrownBy(() -> PropertyReference.of((PersonQuery person) -> {
106105
return ((SuperClass) person).getTenant();
107106
}));
@@ -110,33 +109,33 @@ void switchingOwningTypeFails() {
110109
@Test // GH-3400
111110
void constructorCallsShouldFail() {
112111

113-
assertThatExceptionOfType(TypeParsingException.class)
112+
assertThatExceptionOfType(LambdaIntrospectionException.class)
114113
.isThrownBy(() -> PropertyReference.of((PersonQuery person) -> new PersonQuery(person)));
115114
}
116115

117116
@Test // GH-3400
118117
void enumShouldFail() {
119118

120-
assertThatExceptionOfType(TypeParsingException.class)
119+
assertThatExceptionOfType(LambdaIntrospectionException.class)
121120
.isThrownBy(() -> PropertyReference.of(NotSupported.INSTANCE));
122121
}
123122

124123
@Test // GH-3400
125124
void returningSomethingShouldFail() {
126125

127-
assertThatExceptionOfType(TypeParsingException.class)
126+
assertThatExceptionOfType(LambdaIntrospectionException.class)
128127
.isThrownBy(() -> PropertyReference.of((PropertyReference<Object, Object>) obj -> null));
129-
assertThatExceptionOfType(TypeParsingException.class)
128+
assertThatExceptionOfType(LambdaIntrospectionException.class)
130129
.isThrownBy(() -> PropertyReference.of((PropertyReference<Object, Object>) obj -> 1));
131-
assertThatExceptionOfType(TypeParsingException.class)
130+
assertThatExceptionOfType(LambdaIntrospectionException.class)
132131
.isThrownBy(() -> PropertyReference.of((PropertyReference<Object, Object>) obj -> ""));
133132
}
134133

135134
@Test // GH-3400
136135
@SuppressWarnings("Convert2Lambda")
137136
void classImplementationShouldFail() {
138137

139-
assertThatExceptionOfType(TypeParsingException.class)
138+
assertThatExceptionOfType(LambdaIntrospectionException.class)
140139
.isThrownBy(() -> PropertyReference.of(new PropertyReference<Object, Object>() {
141140
@Override
142141
public @Nullable Object get(Object obj) {
@@ -148,14 +147,14 @@ void classImplementationShouldFail() {
148147
@Test // GH-3400
149148
void constructorMethodReferenceShouldFail() {
150149

151-
assertThatExceptionOfType(TypeParsingException.class)
150+
assertThatExceptionOfType(LambdaIntrospectionException.class)
152151
.isThrownBy(() -> PropertyReference.<PersonQuery, PersonQuery> of(PersonQuery::new));
153152
}
154153

155154
@Test // GH-3400
156155
void failsResolutionWith$StrangeStuff() {
157156

158-
assertThatExceptionOfType(TypeParsingException.class)
157+
assertThatExceptionOfType(LambdaIntrospectionException.class)
159158
.isThrownBy(() -> PropertyReference.of((PersonQuery person) -> {
160159
int a = 1 + 2;
161160
Integer.valueOf(a).toString();
@@ -165,7 +164,7 @@ void constructorMethodReferenceShouldFail() {
165164

166165
@Test // GH-3400
167166
void arithmeticOpsFail() {
168-
assertThatExceptionOfType(TypeParsingException.class).isThrownBy(() -> {
167+
assertThatExceptionOfType(LambdaIntrospectionException.class).isThrownBy(() -> {
169168
PropertyReference.of((PersonQuery person) -> {
170169
int a = 1 + 2;
171170
return person.getName();
@@ -176,7 +175,7 @@ void arithmeticOpsFail() {
176175
@Test // GH-3400
177176
void failsResolvingCallingLocalMethod() {
178177

179-
assertThatExceptionOfType(TypeParsingException.class)
178+
assertThatExceptionOfType(LambdaIntrospectionException.class)
180179
.isThrownBy(() -> PropertyReference.of((PersonQuery person) -> {
181180
failsResolutionWith$StrangeStuff();
182181
return person.getName();

0 commit comments

Comments
 (0)