SPR-8447 Provide sufficient contextwherever possible when exceptions are raised in new @MVC classes.

This commit is contained in:
Rossen Stoyanchev
2011-06-29 15:36:18 +00:00
parent 3a87d8e7cb
commit 0dae1a6bd8
23 changed files with 376 additions and 186 deletions

View File

@@ -64,7 +64,7 @@ public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {
}
Object returnValue = binderMethod.invokeForRequest(request, null, binder);
if (returnValue != null) {
throw new IllegalStateException("This @InitBinder method does not return void: " + binderMethod);
throw new IllegalStateException("@InitBinder methods should return void: " + binderMethod);
}
}
}

View File

@@ -141,7 +141,8 @@ public final class ModelFactory {
if (sessionHandler.isHandlerSessionAttribute(name, parameter.getParameterType())) {
Object attrValue = sessionHandler.retrieveAttribute(request, name);
if (attrValue == null){
throw new HttpSessionRequiredException("Session attribute '" + name + "' not found in session");
throw new HttpSessionRequiredException(
"Session attribute '" + name + "' not found in session: " + requestMethod);
}
mavContainer.addAttribute(name, attrValue);
}

View File

@@ -99,9 +99,9 @@ public abstract class AbstractWebArgumentResolverAdapter implements HandlerMetho
Object result = adaptee.resolveArgument(parameter, webRequest);
if (result == WebArgumentResolver.UNRESOLVED || !ClassUtils.isAssignableValue(paramType, result)) {
throw new IllegalStateException(
"Standard argument type [" + paramType.getName() + "] resolved to incompatible value of type [" +
(result != null ? result.getClass() : null) +
"]. Consider declaring the argument type in a less specific fashion.");
"Standard argument type [" + paramType.getName() + "] in method " + parameter.getMethod() +
"resolved to incompatible value of type [" + (result != null ? result.getClass() : null) +
"]. Consider declaring the argument type in a less specific fashion.");
}
return result;
}

View File

@@ -56,8 +56,9 @@ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolv
}
}
throw new IllegalStateException("Errors/BindingResult argument declared "
+ "without preceding model attribute. Check your handler method signature!");
throw new IllegalStateException(
"An Errors/BindingResult argument must follow a model attribute argument. " +
"Check your handler method signature: " + parameter.getMethod());
}
}

View File

@@ -66,7 +66,7 @@ public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMet
@Override
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException {
throw new UnsupportedOperationException("Did not expect to handle a missing value: an @Value is never required");
throw new UnsupportedOperationException("@Value is never required: " + parameter.getMethod());
}
private static class ExpressionValueNamedValueInfo extends NamedValueInfo {

View File

@@ -16,6 +16,7 @@
package org.springframework.web.method.annotation.support;
import java.lang.reflect.Method;
import java.util.Map;
import org.springframework.core.MethodParameter;
@@ -76,7 +77,9 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
}
else {
// should not happen
throw new UnsupportedOperationException();
Method method = returnType.getMethod();
String returnTypeName = returnType.getParameterType().getName();
throw new UnsupportedOperationException("Unknown return type: " + returnTypeName + " in method: " + method);
}
}
}

View File

@@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
@@ -61,14 +62,8 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver != null) {
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
else {
throw new IllegalStateException(
"No suitable HandlerMethodArgumentResolver found. " +
"supportsParameter(MethodParameter) should have been called previously.");
}
Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
/**

View File

@@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest;
/**
@@ -60,14 +61,8 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
if (handler != null) {
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
else {
throw new IllegalStateException(
"No suitable HandlerMethodReturnValueHandler found. " +
"supportsReturnType(MethodParameter) should have been called previously");
}
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
/**

View File

@@ -142,25 +142,52 @@ public class InvocableHandlerMethod extends HandlerMethod {
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
parameter.initParameterNameDiscovery(parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (this.argumentResolvers.supportsParameter(parameter)) {
args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, dataBinderFactory);
if (argumentResolvers.supportsParameter(parameter)) {
try {
args[i] = argumentResolvers.resolveArgument(parameter, mavContainer, request, dataBinderFactory);
continue;
} catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
}
throw ex;
}
}
else {
throw new IllegalStateException("Cannot resolve argument index=" + parameter.getParameterIndex() + ""
+ ", name=" + parameter.getParameterName() + ", type=" + parameter.getParameterType()
+ " in method " + toString());
if (args[i] == null) {
String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
throw new IllegalStateException(msg);
}
}
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();
}
/**
* Attempt to resolve a method parameter from the list of provided argument values.
*/
@@ -177,55 +204,50 @@ public class InvocableHandlerMethod extends HandlerMethod {
}
/**
* Invoke this handler method with the given argument values.
* Invoke the handler method with the given argument values.
*/
private Object invoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(this.getBridgedMethod());
try {
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
handleIllegalArgumentException(ex, args);
throw ex;
catch (IllegalArgumentException e) {
String msg = getInvocationErrorMessage(e.getMessage(), args);
throw new IllegalArgumentException(msg, e);
}
catch (InvocationTargetException ex) {
handleInvocationTargetException(ex);
throw new IllegalStateException(
"Unexpected exception thrown by method - " + ex.getTargetException().getClass().getName() + ": " +
ex.getTargetException().getMessage());
}
}
private void handleIllegalArgumentException(IllegalArgumentException ex, Object... args) {
StringBuilder builder = new StringBuilder(ex.getMessage());
builder.append(" :: method=").append(getBridgedMethod().toGenericString());
builder.append(" :: invoked with handler type=").append(getBeanType().getName());
if (args != null && args.length > 0) {
builder.append(" and argument types ");
for (int i = 0; i < args.length; i++) {
String argClass = (args[i] != null) ? args[i].getClass().toString() : "null";
builder.append(" : arg[").append(i).append("] ").append(argClass);
catch (InvocationTargetException e) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = e.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
throw new IllegalStateException(msg, targetException);
}
}
else {
builder.append(" and 0 arguments");
}
throw new IllegalArgumentException(builder.toString(), ex);
}
private void handleInvocationTargetException(InvocationTargetException ex) throws Exception {
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
if (targetException instanceof Error) {
throw (Error) targetException;
}
if (targetException instanceof Exception) {
throw (Exception) targetException;
private String getInvocationErrorMessage(String message, Object[] resolvedArgs) {
StringBuilder sb = new StringBuilder(getDetailedErrorMessage(message));
sb.append("Resolved arguments: \n");
for (int i=0; i < resolvedArgs.length; i++) {
sb.append("[").append(i).append("] ");
if (resolvedArgs[i] == null) {
sb.append("[null] \n");
}
else {
sb.append("[type=").append(resolvedArgs[i].getClass().getName()).append("] ");
sb.append("[value=").append(resolvedArgs[i]).append("]\n");
}
}
return sb.toString();
}
}