Revised InvocableHandlerMethod exception messages (controller vs endpoint vs handler)
Introduces dedicated MethodArgumentResolutionException for spring-messaging invocations. Issue: SPR-15139
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +18,7 @@ package org.springframework.messaging.handler.annotation.support;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.handler.invocation.MethodArgumentResolutionException;
|
||||
|
||||
/**
|
||||
* Base class for exceptions resulting from the invocation of
|
||||
@@ -26,43 +26,24 @@ import org.springframework.messaging.MessagingException;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0.3
|
||||
* @deprecated as of 4.3.6, in favor of the invocation-associated
|
||||
* {@link MethodArgumentResolutionException}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class AbstractMethodArgumentResolutionException extends MessagingException {
|
||||
public abstract class AbstractMethodArgumentResolutionException extends MethodArgumentResolutionException {
|
||||
|
||||
private final MethodParameter parameter;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance providing the invalid {@code MethodParameter}.
|
||||
*/
|
||||
protected AbstractMethodArgumentResolutionException(Message<?> message, MethodParameter parameter) {
|
||||
this(message, parameter, getMethodParamMessage(parameter));
|
||||
super(message, parameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance providing the invalid {@code MethodParameter} and
|
||||
* a prepared description. Subclasses should prepend the description with
|
||||
* the help of {@link #getMethodParamMessage(org.springframework.core.MethodParameter)}.
|
||||
*/
|
||||
protected AbstractMethodArgumentResolutionException(Message<?> message, MethodParameter param, String description) {
|
||||
super(message, description);
|
||||
this.parameter = param;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the MethodParameter that was rejected.
|
||||
*/
|
||||
public final MethodParameter getMethodParameter() {
|
||||
return this.parameter;
|
||||
protected AbstractMethodArgumentResolutionException(Message<?> message, MethodParameter parameter, String description) {
|
||||
super(message, parameter, description);
|
||||
}
|
||||
|
||||
|
||||
protected static String getMethodParamMessage(MethodParameter param) {
|
||||
return new StringBuilder("Could not resolve method parameter at index ")
|
||||
.append(param.getParameterIndex()).append(" in method: ")
|
||||
.append(param.getMethod().toGenericString()).append(" ").toString();
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,27 +29,25 @@ import org.springframework.validation.ObjectError;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@SuppressWarnings({"serial", "deprecation"})
|
||||
public class MethodArgumentNotValidException extends AbstractMethodArgumentResolutionException {
|
||||
|
||||
private final BindingResult bindingResult;
|
||||
private BindingResult bindingResult;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance with the invalid {@code MethodParameter}.
|
||||
*/
|
||||
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter) {
|
||||
this(message, parameter, null);
|
||||
super(message, parameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with the invalid {@code MethodParameter} and a
|
||||
* {@link org.springframework.validation.BindingResult}.
|
||||
*/
|
||||
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter,
|
||||
BindingResult bindingResult) {
|
||||
|
||||
super(message, parameter, getMethodParamMessage(parameter) + getValidationErrorMessage(bindingResult));
|
||||
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter, BindingResult bindingResult) {
|
||||
super(message, parameter, getValidationErrorMessage(bindingResult));
|
||||
this.bindingResult = bindingResult;
|
||||
}
|
||||
|
||||
@@ -64,17 +62,12 @@ public class MethodArgumentNotValidException extends AbstractMethodArgumentResol
|
||||
|
||||
|
||||
private static String getValidationErrorMessage(BindingResult bindingResult) {
|
||||
if (bindingResult != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(", with ").append(bindingResult.getErrorCount()).append(" error(s): ");
|
||||
for (ObjectError error : bindingResult.getAllErrors()) {
|
||||
sb.append("[").append(error).append("] ");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(bindingResult.getErrorCount()).append(" error(s): ");
|
||||
for (ObjectError error : bindingResult.getAllErrors()) {
|
||||
sb.append("[").append(error).append("] ");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,20 +20,16 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* Exception that indicates that a method argument has not the
|
||||
* expected type.
|
||||
* Exception that indicates that a method argument has not the expected type.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.0.3
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@SuppressWarnings({"serial", "deprecation"})
|
||||
public class MethodArgumentTypeMismatchException extends AbstractMethodArgumentResolutionException {
|
||||
|
||||
/**
|
||||
* Create a new instance with the invalid {@code MethodParameter}.
|
||||
*/
|
||||
public MethodArgumentTypeMismatchException(Message<?> message, MethodParameter param, String description) {
|
||||
super(message, param, getMethodParamMessage(param) + description);
|
||||
public MethodArgumentTypeMismatchException(Message<?> message, MethodParameter parameter, String description) {
|
||||
super(message, parameter, description);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -139,36 +139,22 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
|
||||
logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
if (args[i] == null) {
|
||||
String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
|
||||
throw new IllegalStateException(msg);
|
||||
throw new MethodArgumentResolutionException(message, parameter,
|
||||
getArgumentResolutionErrorMessage("No suitable resolver for", i));
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
private String getArgumentResolutionErrorMessage(String message, int index) {
|
||||
MethodParameter param = getMethodParameters()[index];
|
||||
message += " [" + index + "] [type=" + param.getParameterType().getName() + "]";
|
||||
return getDetailedErrorMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds HandlerMethod details such as the controller type and method
|
||||
* signature to the given error message.
|
||||
* @param message error message to append the HandlerMethod details to
|
||||
*/
|
||||
protected String getDetailedErrorMessage(String message) {
|
||||
StringBuilder sb = new StringBuilder(message).append("\n");
|
||||
sb.append("HandlerMethod details: \n");
|
||||
sb.append("Controller [").append(getBeanType().getName()).append("]\n");
|
||||
sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n");
|
||||
return sb.toString();
|
||||
private String getArgumentResolutionErrorMessage(String text, int index) {
|
||||
Class<?> paramType = getMethodParameters()[index].getParameterType();
|
||||
return text + " argument " + index + " of type '" + paramType.getName() + "'";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,8 +183,8 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
assertTargetBean(getBridgedMethod(), getBean(), args);
|
||||
String message = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
|
||||
throw new IllegalStateException(getInvocationErrorMessage(message, args), ex);
|
||||
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
|
||||
throw new IllegalStateException(getInvocationErrorMessage(text, args), ex);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
// Unwrap for HandlerExceptionResolvers ...
|
||||
@@ -213,33 +199,33 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
throw (Exception) targetException;
|
||||
}
|
||||
else {
|
||||
String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
|
||||
throw new IllegalStateException(msg, targetException);
|
||||
String text = getInvocationErrorMessage("Failed to invoke handler method", args);
|
||||
throw new IllegalStateException(text, targetException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the target bean class is an instance of the class where the given
|
||||
* method is declared. In some cases the actual controller instance at request-
|
||||
* method is declared. In some cases the actual endpoint instance at request-
|
||||
* processing time may be a JDK dynamic proxy (lazy initialization, prototype
|
||||
* beans, and others). {@code @Controller}'s that require proxying should prefer
|
||||
* beans, and others). Endpoint classes that require proxying should prefer
|
||||
* class-based proxy mechanisms.
|
||||
*/
|
||||
private void assertTargetBean(Method method, Object targetBean, Object[] args) {
|
||||
Class<?> methodDeclaringClass = method.getDeclaringClass();
|
||||
Class<?> targetBeanClass = targetBean.getClass();
|
||||
if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) {
|
||||
String msg = "The mapped controller method class '" + methodDeclaringClass.getName() +
|
||||
"' is not an instance of the actual controller bean class '" +
|
||||
targetBeanClass.getName() + "'. If the controller requires proxying " +
|
||||
String text = "The mapped handler method class '" + methodDeclaringClass.getName() +
|
||||
"' is not an instance of the actual endpoint bean class '" +
|
||||
targetBeanClass.getName() + "'. If the endpoint requires proxying " +
|
||||
"(e.g. due to @Transactional), please use class-based proxying.";
|
||||
throw new IllegalStateException(getInvocationErrorMessage(msg, args));
|
||||
throw new IllegalStateException(getInvocationErrorMessage(text, args));
|
||||
}
|
||||
}
|
||||
|
||||
private String getInvocationErrorMessage(String message, Object[] resolvedArgs) {
|
||||
StringBuilder sb = new StringBuilder(getDetailedErrorMessage(message));
|
||||
private String getInvocationErrorMessage(String text, Object[] resolvedArgs) {
|
||||
StringBuilder sb = new StringBuilder(getDetailedErrorMessage(text));
|
||||
sb.append("Resolved arguments: \n");
|
||||
for (int i = 0; i < resolvedArgs.length; i++) {
|
||||
sb.append("[").append(i).append("] ");
|
||||
@@ -254,6 +240,19 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds HandlerMethod details such as the bean type and method signature to the message.
|
||||
* @param text error message to append the HandlerMethod details to
|
||||
*/
|
||||
protected String getDetailedErrorMessage(String text) {
|
||||
StringBuilder sb = new StringBuilder(text).append("\n");
|
||||
sb.append("HandlerMethod details: \n");
|
||||
sb.append("Endpoint [").append(getBeanType().getName()).append("]\n");
|
||||
sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
MethodParameter getAsyncReturnValueType(Object returnValue) {
|
||||
return new AsyncResultMethodParameter(returnValue);
|
||||
}
|
||||
@@ -268,7 +267,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
public AsyncResultMethodParameter(Object returnValue) {
|
||||
super(-1);
|
||||
this.returnValue = returnValue;
|
||||
this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric(0);
|
||||
this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric();
|
||||
}
|
||||
|
||||
protected AsyncResultMethodParameter(AsyncResultMethodParameter original) {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.messaging.handler.invocation;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
|
||||
/**
|
||||
* Common exception resulting from the invocation of
|
||||
* {@link org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.3.6
|
||||
* @see HandlerMethodArgumentResolver
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class MethodArgumentResolutionException extends MessagingException {
|
||||
|
||||
private final MethodParameter parameter;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance providing the invalid {@code MethodParameter}.
|
||||
*/
|
||||
public MethodArgumentResolutionException(Message<?> message, MethodParameter parameter) {
|
||||
super(message, getMethodParameterMessage(parameter));
|
||||
this.parameter = parameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance providing the invalid {@code MethodParameter} and
|
||||
* a prepared description.
|
||||
*/
|
||||
public MethodArgumentResolutionException(Message<?> message, MethodParameter parameter, String description) {
|
||||
super(message, getMethodParameterMessage(parameter) + ": " + description);
|
||||
this.parameter = parameter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the MethodParameter that was rejected.
|
||||
*/
|
||||
public final MethodParameter getMethodParameter() {
|
||||
return this.parameter;
|
||||
}
|
||||
|
||||
|
||||
private static String getMethodParameterMessage(MethodParameter parameter) {
|
||||
return "Could not resolve method parameter at index " + parameter.getParameterIndex() +
|
||||
" in " + parameter.getMethod().toGenericString();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user