Skip to content

Commit 047786a

Browse files
committed
Revised InvocableHandlerMethod exception messages (controller vs endpoint vs handler)
Introduces dedicated MethodArgumentResolutionException for spring-messaging invocations. Issue: SPR-15139
1 parent 74596a6 commit 047786a

File tree

10 files changed

+223
-188
lines changed

10 files changed

+223
-188
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2017 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.
@@ -18,51 +18,32 @@
1818

1919
import org.springframework.core.MethodParameter;
2020
import org.springframework.messaging.Message;
21-
import org.springframework.messaging.MessagingException;
21+
import org.springframework.messaging.handler.invocation.MethodArgumentResolutionException;
2222

2323
/**
2424
* Base class for exceptions resulting from the invocation of
2525
* {@link org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver}.
2626
*
2727
* @author Rossen Stoyanchev
2828
* @since 4.0.3
29+
* @deprecated as of 4.3.6, in favor of the invocation-associated
30+
* {@link MethodArgumentResolutionException}
2931
*/
32+
@Deprecated
3033
@SuppressWarnings("serial")
31-
public abstract class AbstractMethodArgumentResolutionException extends MessagingException {
34+
public abstract class AbstractMethodArgumentResolutionException extends MethodArgumentResolutionException {
3235

33-
private final MethodParameter parameter;
34-
35-
36-
/**
37-
* Create a new instance providing the invalid {@code MethodParameter}.
38-
*/
3936
protected AbstractMethodArgumentResolutionException(Message<?> message, MethodParameter parameter) {
40-
this(message, parameter, getMethodParamMessage(parameter));
37+
super(message, parameter);
4138
}
4239

43-
/**
44-
* Create a new instance providing the invalid {@code MethodParameter} and
45-
* a prepared description. Subclasses should prepend the description with
46-
* the help of {@link #getMethodParamMessage(org.springframework.core.MethodParameter)}.
47-
*/
48-
protected AbstractMethodArgumentResolutionException(Message<?> message, MethodParameter param, String description) {
49-
super(message, description);
50-
this.parameter = param;
51-
}
52-
53-
54-
/**
55-
* Return the MethodParameter that was rejected.
56-
*/
57-
public final MethodParameter getMethodParameter() {
58-
return this.parameter;
40+
protected AbstractMethodArgumentResolutionException(Message<?> message, MethodParameter parameter, String description) {
41+
super(message, parameter, description);
5942
}
6043

6144

6245
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()).append(" ").toString();
46+
return "";
6647
}
6748

6849
}

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

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2017 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.
@@ -29,27 +29,25 @@
2929
* @author Rossen Stoyanchev
3030
* @since 4.0.1
3131
*/
32-
@SuppressWarnings("serial")
32+
@SuppressWarnings({"serial", "deprecation"})
3333
public class MethodArgumentNotValidException extends AbstractMethodArgumentResolutionException {
3434

35-
private final BindingResult bindingResult;
35+
private BindingResult bindingResult;
3636

3737

3838
/**
3939
* Create a new instance with the invalid {@code MethodParameter}.
4040
*/
4141
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter) {
42-
this(message, parameter, null);
42+
super(message, parameter);
4343
}
4444

4545
/**
4646
* Create a new instance with the invalid {@code MethodParameter} and a
4747
* {@link org.springframework.validation.BindingResult}.
4848
*/
49-
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter,
50-
BindingResult bindingResult) {
51-
52-
super(message, parameter, getMethodParamMessage(parameter) + getValidationErrorMessage(bindingResult));
49+
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter, BindingResult bindingResult) {
50+
super(message, parameter, getValidationErrorMessage(bindingResult));
5351
this.bindingResult = bindingResult;
5452
}
5553

@@ -64,17 +62,12 @@ public final BindingResult getBindingResult() {
6462

6563

6664
private static String getValidationErrorMessage(BindingResult bindingResult) {
67-
if (bindingResult != null) {
68-
StringBuilder sb = new StringBuilder();
69-
sb.append(", with ").append(bindingResult.getErrorCount()).append(" error(s): ");
70-
for (ObjectError error : bindingResult.getAllErrors()) {
71-
sb.append("[").append(error).append("] ");
72-
}
73-
return sb.toString();
74-
}
75-
else {
76-
return "";
65+
StringBuilder sb = new StringBuilder();
66+
sb.append(bindingResult.getErrorCount()).append(" error(s): ");
67+
for (ObjectError error : bindingResult.getAllErrors()) {
68+
sb.append("[").append(error).append("] ");
7769
}
70+
return sb.toString();
7871
}
7972

8073
}

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2017 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.
@@ -20,20 +20,16 @@
2020
import org.springframework.messaging.Message;
2121

2222
/**
23-
* Exception that indicates that a method argument has not the
24-
* expected type.
23+
* Exception that indicates that a method argument has not the expected type.
2524
*
2625
* @author Stephane Nicoll
2726
* @since 4.0.3
2827
*/
29-
@SuppressWarnings("serial")
28+
@SuppressWarnings({"serial", "deprecation"})
3029
public class MethodArgumentTypeMismatchException extends AbstractMethodArgumentResolutionException {
3130

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

3935
}

spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 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.
@@ -139,36 +139,22 @@ private Object[] getMethodArgumentValues(Message<?> message, Object... providedA
139139
}
140140
catch (Exception ex) {
141141
if (logger.isDebugEnabled()) {
142-
logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
142+
logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
143143
}
144144
throw ex;
145145
}
146146
}
147147
if (args[i] == null) {
148-
String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
149-
throw new IllegalStateException(msg);
148+
throw new MethodArgumentResolutionException(message, parameter,
149+
getArgumentResolutionErrorMessage("No suitable resolver for", i));
150150
}
151151
}
152152
return args;
153153
}
154154

155-
private String getArgumentResolutionErrorMessage(String message, int index) {
156-
MethodParameter param = getMethodParameters()[index];
157-
message += " [" + index + "] [type=" + param.getParameterType().getName() + "]";
158-
return getDetailedErrorMessage(message);
159-
}
160-
161-
/**
162-
* Adds HandlerMethod details such as the controller type and method
163-
* signature to the given error message.
164-
* @param message error message to append the HandlerMethod details to
165-
*/
166-
protected String getDetailedErrorMessage(String message) {
167-
StringBuilder sb = new StringBuilder(message).append("\n");
168-
sb.append("HandlerMethod details: \n");
169-
sb.append("Controller [").append(getBeanType().getName()).append("]\n");
170-
sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n");
171-
return sb.toString();
155+
private String getArgumentResolutionErrorMessage(String text, int index) {
156+
Class<?> paramType = getMethodParameters()[index].getParameterType();
157+
return text + " argument " + index + " of type '" + paramType.getName() + "'";
172158
}
173159

174160
/**
@@ -197,8 +183,8 @@ protected Object doInvoke(Object... args) throws Exception {
197183
}
198184
catch (IllegalArgumentException ex) {
199185
assertTargetBean(getBridgedMethod(), getBean(), args);
200-
String message = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
201-
throw new IllegalStateException(getInvocationErrorMessage(message, args), ex);
186+
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
187+
throw new IllegalStateException(getInvocationErrorMessage(text, args), ex);
202188
}
203189
catch (InvocationTargetException ex) {
204190
// Unwrap for HandlerExceptionResolvers ...
@@ -213,33 +199,33 @@ else if (targetException instanceof Exception) {
213199
throw (Exception) targetException;
214200
}
215201
else {
216-
String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
217-
throw new IllegalStateException(msg, targetException);
202+
String text = getInvocationErrorMessage("Failed to invoke handler method", args);
203+
throw new IllegalStateException(text, targetException);
218204
}
219205
}
220206
}
221207

222208
/**
223209
* Assert that the target bean class is an instance of the class where the given
224-
* method is declared. In some cases the actual controller instance at request-
210+
* method is declared. In some cases the actual endpoint instance at request-
225211
* processing time may be a JDK dynamic proxy (lazy initialization, prototype
226-
* beans, and others). {@code @Controller}'s that require proxying should prefer
212+
* beans, and others). Endpoint classes that require proxying should prefer
227213
* class-based proxy mechanisms.
228214
*/
229215
private void assertTargetBean(Method method, Object targetBean, Object[] args) {
230216
Class<?> methodDeclaringClass = method.getDeclaringClass();
231217
Class<?> targetBeanClass = targetBean.getClass();
232218
if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) {
233-
String msg = "The mapped controller method class '" + methodDeclaringClass.getName() +
234-
"' is not an instance of the actual controller bean class '" +
235-
targetBeanClass.getName() + "'. If the controller requires proxying " +
219+
String text = "The mapped handler method class '" + methodDeclaringClass.getName() +
220+
"' is not an instance of the actual endpoint bean class '" +
221+
targetBeanClass.getName() + "'. If the endpoint requires proxying " +
236222
"(e.g. due to @Transactional), please use class-based proxying.";
237-
throw new IllegalStateException(getInvocationErrorMessage(msg, args));
223+
throw new IllegalStateException(getInvocationErrorMessage(text, args));
238224
}
239225
}
240226

241-
private String getInvocationErrorMessage(String message, Object[] resolvedArgs) {
242-
StringBuilder sb = new StringBuilder(getDetailedErrorMessage(message));
227+
private String getInvocationErrorMessage(String text, Object[] resolvedArgs) {
228+
StringBuilder sb = new StringBuilder(getDetailedErrorMessage(text));
243229
sb.append("Resolved arguments: \n");
244230
for (int i = 0; i < resolvedArgs.length; i++) {
245231
sb.append("[").append(i).append("] ");
@@ -254,6 +240,19 @@ private String getInvocationErrorMessage(String message, Object[] resolvedArgs)
254240
return sb.toString();
255241
}
256242

243+
/**
244+
* Adds HandlerMethod details such as the bean type and method signature to the message.
245+
* @param text error message to append the HandlerMethod details to
246+
*/
247+
protected String getDetailedErrorMessage(String text) {
248+
StringBuilder sb = new StringBuilder(text).append("\n");
249+
sb.append("HandlerMethod details: \n");
250+
sb.append("Endpoint [").append(getBeanType().getName()).append("]\n");
251+
sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n");
252+
return sb.toString();
253+
}
254+
255+
257256
MethodParameter getAsyncReturnValueType(Object returnValue) {
258257
return new AsyncResultMethodParameter(returnValue);
259258
}
@@ -268,7 +267,7 @@ private class AsyncResultMethodParameter extends HandlerMethodParameter {
268267
public AsyncResultMethodParameter(Object returnValue) {
269268
super(-1);
270269
this.returnValue = returnValue;
271-
this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric(0);
270+
this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric();
272271
}
273272

274273
protected AsyncResultMethodParameter(AsyncResultMethodParameter original) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2002-2017 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.invocation;
18+
19+
import org.springframework.core.MethodParameter;
20+
import org.springframework.messaging.Message;
21+
import org.springframework.messaging.MessagingException;
22+
23+
/**
24+
* Common exception resulting from the invocation of
25+
* {@link org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver}.
26+
*
27+
* @author Juergen Hoeller
28+
* @since 4.3.6
29+
* @see HandlerMethodArgumentResolver
30+
*/
31+
@SuppressWarnings("serial")
32+
public class MethodArgumentResolutionException extends MessagingException {
33+
34+
private final MethodParameter parameter;
35+
36+
37+
/**
38+
* Create a new instance providing the invalid {@code MethodParameter}.
39+
*/
40+
public MethodArgumentResolutionException(Message<?> message, MethodParameter parameter) {
41+
super(message, getMethodParameterMessage(parameter));
42+
this.parameter = parameter;
43+
}
44+
45+
/**
46+
* Create a new instance providing the invalid {@code MethodParameter} and
47+
* a prepared description.
48+
*/
49+
public MethodArgumentResolutionException(Message<?> message, MethodParameter parameter, String description) {
50+
super(message, getMethodParameterMessage(parameter) + ": " + description);
51+
this.parameter = parameter;
52+
}
53+
54+
55+
/**
56+
* Return the MethodParameter that was rejected.
57+
*/
58+
public final MethodParameter getMethodParameter() {
59+
return this.parameter;
60+
}
61+
62+
63+
private static String getMethodParameterMessage(MethodParameter parameter) {
64+
return "Could not resolve method parameter at index " + parameter.getParameterIndex() +
65+
" in " + parameter.getMethod().toGenericString();
66+
}
67+
68+
}

0 commit comments

Comments
 (0)