Skip to content

Commit 2c1d5ef

Browse files
committed
Introduce base exception class for arg resolution
Issue: SPR-11584
1 parent 2aee0d8 commit 2c1d5ef

File tree

5 files changed

+143
-75
lines changed

5 files changed

+143
-75
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2002-2014 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+
* http://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+
17+
package org.springframework.messaging.handler.annotation.support;
18+
19+
import org.springframework.core.MethodParameter;
20+
import org.springframework.messaging.Message;
21+
import org.springframework.messaging.MessagingException;
22+
23+
/**
24+
* Base class for exceptions resulting from the invocation of
25+
* {@link org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver}.
26+
*
27+
* @author Rossen Stoyanchev
28+
* @since 4.0.3
29+
*/
30+
@SuppressWarnings("serial")
31+
public abstract class AbstractMethodArgumentResolutionException extends MessagingException {
32+
33+
private final MethodParameter parameter;
34+
35+
36+
/**
37+
* Create a new instance providing the invalid {@code MethodParameter}.
38+
*/
39+
protected AbstractMethodArgumentResolutionException(Message<?> message, MethodParameter parameter) {
40+
this(message, parameter, getMethodParamMessage(parameter));
41+
}
42+
43+
/**
44+
* Create a new instance providing the invalid {@code MethodParameter} and
45+
* a prepared description. Sub-classes should prepend the description with
46+
* the help of {@link #getMethodParamMessage(org.springframework.core.MethodParameter)}.
47+
*/
48+
protected AbstractMethodArgumentResolutionException(Message<?> message,
49+
MethodParameter parameter, String description) {
50+
51+
super(message, description);
52+
this.parameter = parameter;
53+
}
54+
55+
/**
56+
* Return the MethodParameter that was rejected.
57+
*/
58+
public MethodParameter getMethodParameter() {
59+
return this.parameter;
60+
}
61+
62+
protected static String getMethodParamMessage(MethodParameter param) {
63+
return new StringBuilder("Could not resolve method parameter at index ")
64+
.append(param.getParameterIndex()).append(" in method: ")
65+
.append(param.getMethod().toGenericString()).toString();
66+
}
67+
68+
}

spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolver.java

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 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.
@@ -21,6 +21,8 @@
2121
import org.springframework.messaging.Message;
2222
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
2323

24+
import java.lang.reflect.Type;
25+
2426
/**
2527
* A {@link HandlerMethodArgumentResolver} for {@link Message} parameters. Validates
2628
* that the generic type of the payload matches with the message value.
@@ -39,28 +41,30 @@ public boolean supportsParameter(MethodParameter parameter) {
3941

4042
@Override
4143
public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
42-
// Validate the message type is assignable
43-
if (!parameter.getParameterType().isAssignableFrom(message.getClass())) {
44-
throw new MethodArgumentTypeMismatchException(message,
45-
"Could not resolve Message parameter: invalid message type:"
46-
+ "expected [" + message.getClass().getName() + "] but got ["
47-
+ parameter.getParameterType().getName() + "]");
44+
45+
Class<?> paramType = parameter.getParameterType();
46+
47+
if (!paramType.isAssignableFrom(message.getClass())) {
48+
throw new MethodArgumentTypeMismatchException(message, parameter,
49+
"The actual message type [" + message.getClass().getName() + "] " +
50+
"does not match the expected type [" + paramType.getName() + "]");
4851
}
4952

50-
// validate that the payload type matches
51-
Class<?> effectivePayloadType = getPayloadType(parameter);
52-
if (effectivePayloadType != null && !effectivePayloadType.isInstance(message.getPayload())) {
53-
throw new MethodArgumentTypeMismatchException(message,
54-
"Could not resolve Message parameter: invalid payload type: "
55-
+ "expected [" + effectivePayloadType.getName() + "] but got ["
56-
+ message.getPayload().getClass().getName() + "]");
53+
Class<?> expectedPayloadType = getPayloadType(parameter);
54+
Object payload = message.getPayload();
55+
56+
if (expectedPayloadType != null && !expectedPayloadType.isInstance(payload)) {
57+
throw new MethodArgumentTypeMismatchException(message, parameter,
58+
"The expected Message<?> payload type [" + expectedPayloadType.getClass().getName() +
59+
"] does not match the actual payload type [" + payload.getClass().getName() + "]");
5760
}
61+
5862
return message;
5963
}
6064

6165
private Class<?> getPayloadType(MethodParameter parameter) {
62-
ResolvableType resolvableType = ResolvableType
63-
.forType(parameter.getGenericParameterType()).as(Message.class);
66+
Type genericParamType = parameter.getGenericParameterType();
67+
ResolvableType resolvableType = ResolvableType.forType(genericParamType).as(Message.class);
6468
return resolvableType.getGeneric(0).resolve(Object.class);
6569
}
6670

spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentNotValidException.java

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,26 @@
1818

1919
import org.springframework.core.MethodParameter;
2020
import org.springframework.messaging.Message;
21-
import org.springframework.messaging.MessagingException;
2221
import org.springframework.validation.BindingResult;
2322
import org.springframework.validation.ObjectError;
2423

2524
/**
26-
* Exception to be thrown when a method argument is not valid. For instance, this
27-
* can be issued if a validation on a method parameter annotated with
28-
* {@code @Valid} fails.
25+
* Exception to be thrown when a method argument fails validation perhaps as a
26+
* result of {@code @Valid} style validation, or perhaps because it is required.
2927
*
3028
* @author Brian Clozel
29+
* @author Rossen Stoyanchev
3130
* @since 4.0.1
3231
*/
3332
@SuppressWarnings("serial")
34-
public class MethodArgumentNotValidException extends MessagingException {
35-
36-
private final MethodParameter parameter;
33+
public class MethodArgumentNotValidException extends AbstractMethodArgumentResolutionException {
3734

3835
private final BindingResult bindingResult;
3936

4037

4138
/**
4239
* Create a new instance with the invalid {@code MethodParameter}.
4340
*/
44-
4541
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter) {
4642
this(message, parameter, null);
4743
}
@@ -53,19 +49,13 @@ public MethodArgumentNotValidException(Message<?> message, MethodParameter param
5349
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter,
5450
BindingResult bindingResult) {
5551

56-
super(message, generateMessage(parameter, bindingResult));
57-
this.parameter = parameter;
52+
super(message, parameter, getMethodParamMessage(parameter) +
53+
getValidationErrorMessage(parameter, bindingResult));
54+
5855
this.bindingResult = bindingResult;
5956
}
6057

6158

62-
/**
63-
* Return the MethodParameter that was rejected.
64-
*/
65-
public MethodParameter getMethodParameter() {
66-
return this.parameter;
67-
}
68-
6959
/**
7060
* Return the BindingResult if the failure is validation-related or {@code null}.
7161
*/
@@ -74,21 +64,18 @@ public BindingResult getBindingResult() {
7464
}
7565

7666

77-
private static String generateMessage(MethodParameter parameter, BindingResult bindingResult) {
78-
79-
StringBuilder sb = new StringBuilder("Invalid parameter at index ")
80-
.append(parameter.getParameterIndex()).append(" in method: ")
81-
.append(parameter.getMethod().toGenericString());
82-
83-
67+
private static String getValidationErrorMessage(MethodParameter parameter, BindingResult bindingResult) {
8468
if (bindingResult != null) {
69+
StringBuilder sb = new StringBuilder();
8570
sb.append(", with ").append(bindingResult.getErrorCount()).append(" error(s): ");
8671
for (ObjectError error : bindingResult.getAllErrors()) {
8772
sb.append("[").append(error).append("] ");
8873
}
74+
return sb.toString();
75+
}
76+
else {
77+
return "";
8978
}
90-
91-
return sb.toString();
9279
}
9380

9481
}

spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentTypeMismatchException.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.messaging.handler.annotation.support;
1818

19+
import org.springframework.core.MethodParameter;
1920
import org.springframework.messaging.Message;
2021
import org.springframework.messaging.MessagingException;
2122

@@ -26,9 +27,14 @@
2627
* @author Stephane Nicoll
2728
* @since 4.0.3
2829
*/
29-
public class MethodArgumentTypeMismatchException extends MessagingException {
30+
@SuppressWarnings("serial")
31+
public class MethodArgumentTypeMismatchException extends AbstractMethodArgumentResolutionException {
3032

31-
public MethodArgumentTypeMismatchException(Message<?> message, String description) {
32-
super(message, description);
33+
34+
/**
35+
* Create a new instance with the invalid {@code MethodParameter}.
36+
*/
37+
public MethodArgumentTypeMismatchException(Message<?> message, MethodParameter parameter, String description) {
38+
super(message, parameter, getMethodParamMessage(parameter) + description);
3339
}
3440
}

spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolverTests.java

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import org.springframework.messaging.support.MessageBuilder;
3434

3535
/**
36+
* Unit tests for
37+
* {@link org.springframework.messaging.handler.annotation.support.MessageMethodArgumentResolver}.
3638
*
3739
* @author Stephane Nicoll
3840
*/
@@ -45,101 +47,102 @@ public class MessageMethodArgumentResolverTests {
4547

4648
private Method method;
4749

50+
4851
@Before
4952
public void setup() throws Exception {
50-
method = MessageMethodArgumentResolverTests.class.getDeclaredMethod("handleMessage",
53+
this.method = MessageMethodArgumentResolverTests.class.getDeclaredMethod("handleMessage",
5154
Message.class, Message.class, Message.class, Message.class, ErrorMessage.class);
5255
}
5356

57+
5458
@Test
5559
public void resolveAnyPayloadType() throws Exception {
5660
Message<String> message = MessageBuilder.withPayload("test").build();
57-
MethodParameter parameter = new MethodParameter(method, 0);
61+
MethodParameter parameter = new MethodParameter(this.method, 0);
5862

59-
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
60-
assertSame(message, resolver.resolveArgument(parameter, message));
63+
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
64+
assertSame(message, this.resolver.resolveArgument(parameter, message));
6165
}
6266

6367
@Test
6468
public void resolvePayloadTypeExactType() throws Exception {
6569
Message<Integer> message = MessageBuilder.withPayload(123).build();
66-
MethodParameter parameter = new MethodParameter(method, 1);
70+
MethodParameter parameter = new MethodParameter(this.method, 1);
6771

68-
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
69-
assertSame(message, resolver.resolveArgument(parameter, message));
72+
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
73+
assertSame(message, this.resolver.resolveArgument(parameter, message));
7074
}
7175

7276
@Test
7377
public void resolvePayloadTypeSubClass() throws Exception {
7478
Message<Integer> message = MessageBuilder.withPayload(123).build();
75-
MethodParameter parameter = new MethodParameter(method, 2);
79+
MethodParameter parameter = new MethodParameter(this.method, 2);
7680

77-
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
78-
assertSame(message, resolver.resolveArgument(parameter, message));
81+
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
82+
assertSame(message, this.resolver.resolveArgument(parameter, message));
7983
}
8084

8185
@Test
8286
public void resolveInvalidPayloadType() throws Exception {
8387
Message<String> message = MessageBuilder.withPayload("test").build();
84-
MethodParameter parameter = new MethodParameter(method, 1);
88+
MethodParameter parameter = new MethodParameter(this.method, 1);
8589

86-
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
90+
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
8791
thrown.expect(MethodArgumentTypeMismatchException.class);
8892
thrown.expectMessage(Integer.class.getName());
8993
thrown.expectMessage(String.class.getName());
90-
resolver.resolveArgument(parameter, message);
94+
this.resolver.resolveArgument(parameter, message);
9195
}
9296

9397
@Test
9498
public void resolveUpperBoundPayloadType() throws Exception {
9599
Message<Integer> message = MessageBuilder.withPayload(123).build();
96-
MethodParameter parameter = new MethodParameter(method, 3);
100+
MethodParameter parameter = new MethodParameter(this.method, 3);
97101

98-
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
99-
assertSame(message, resolver.resolveArgument(parameter, message));
102+
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
103+
assertSame(message, this.resolver.resolveArgument(parameter, message));
100104
}
101105

102106
@Test
103107
public void resolveOutOfBoundPayloadType() throws Exception {
104108
Message<Locale> message = MessageBuilder.withPayload(Locale.getDefault()).build();
105-
MethodParameter parameter = new MethodParameter(method, 3);
109+
MethodParameter parameter = new MethodParameter(this.method, 3);
106110

107-
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
111+
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
108112
thrown.expect(MethodArgumentTypeMismatchException.class);
109113
thrown.expectMessage(Number.class.getName());
110114
thrown.expectMessage(Locale.class.getName());
111-
resolver.resolveArgument(parameter, message);
115+
this.resolver.resolveArgument(parameter, message);
112116
}
113117

114118
@Test
115119
public void resolveMessageSubTypeExactMatch() throws Exception {
116120
ErrorMessage message = new ErrorMessage(new UnsupportedOperationException());
117-
MethodParameter parameter = new MethodParameter(method, 4);
121+
MethodParameter parameter = new MethodParameter(this.method, 4);
118122

119-
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
120-
assertSame(message, resolver.resolveArgument(parameter, message));
123+
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
124+
assertSame(message, this.resolver.resolveArgument(parameter, message));
121125
}
122126

123127
@Test
124128
public void resolveMessageSubTypeSubClass() throws Exception {
125129
ErrorMessage message = new ErrorMessage(new UnsupportedOperationException());
126-
MethodParameter parameter = new MethodParameter(method, 0);
130+
MethodParameter parameter = new MethodParameter(this.method, 0);
127131

128-
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
129-
assertSame(message, resolver.resolveArgument(parameter, message));
132+
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
133+
assertSame(message, this.resolver.resolveArgument(parameter, message));
130134
}
131135

132136
@Test
133137
public void resolveWrongMessageType() throws Exception {
134-
Message<? extends Throwable> message = new GenericMessage<Throwable>(
135-
new UnsupportedOperationException());
136-
MethodParameter parameter = new MethodParameter(method, 4);
138+
Message<? extends Throwable> message = new GenericMessage<Throwable>(new UnsupportedOperationException());
139+
MethodParameter parameter = new MethodParameter(this.method, 4);
137140

138-
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
141+
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
139142
thrown.expect(MethodArgumentTypeMismatchException.class);
140143
thrown.expectMessage(ErrorMessage.class.getName());
141144
thrown.expectMessage(GenericMessage.class.getName());
142-
assertSame(message, resolver.resolveArgument(parameter, message));
145+
assertSame(message, this.resolver.resolveArgument(parameter, message));
143146
}
144147

145148
@SuppressWarnings("unused")

0 commit comments

Comments
 (0)