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

@@ -73,7 +73,7 @@ public class HandlerMethodArgumentResolverCompositeTests {
assertEquals("Didn't use the first registered resolver", Integer.valueOf(1), resolvedValue);
}
@Test(expected=IllegalStateException.class)
@Test(expected=IllegalArgumentException.class)
public void noSuitableArgumentResolver() throws Exception {
this.resolvers.resolveArgument(paramStr, null, null, null);
}

View File

@@ -74,7 +74,7 @@ public class HandlerMethodReturnValueHandlerCompositeTests {
assertNull("Shouldn't have use the 2nd registered handler", h2.getReturnValue());
}
@Test(expected=IllegalStateException.class)
@Test(expected=IllegalArgumentException.class)
public void noSuitableReturnValueHandler() throws Exception {
registerHandler(Integer.class);
this.handlers.handleReturnValue("value", paramStr, null, null);

View File

@@ -17,89 +17,223 @@
package org.springframework.web.method.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.reflect.Method;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.MethodParameter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
/**
* Test fixture with {@link InvocableHandlerMethod}.
* Test fixture for {@link InvocableHandlerMethod} unit tests.
*
* @author Rossen Stoyanchev
*/
public class InvocableHandlerMethodTests {
private HandlerMethodArgumentResolverComposite argumentResolvers;
private InvocableHandlerMethod handleMethod;
private NativeWebRequest webRequest;
@Before
public void setUp() throws Exception {
argumentResolvers = new HandlerMethodArgumentResolverComposite();
Method method = Handler.class.getDeclaredMethod("handle", Integer.class, String.class);
this.handleMethod = new InvocableHandlerMethod(new Handler(), method);
this.webRequest = new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse());
}
@Test
public void resolveArgument() throws Exception {
StubArgumentResolver intResolver = addResolver(Integer.class, 99);
StubArgumentResolver strResolver = addResolver(String.class, "value");
InvocableHandlerMethod method = invocableHandlerMethod("handle", Integer.class, String.class);
Object returnValue = method.invokeForRequest(webRequest, null);
assertEquals("Integer resolver not invoked", 1, intResolver.getResolvedParameters().size());
assertEquals("String resolver not invoked", 1, strResolver.getResolvedParameters().size());
assertEquals("Invalid return value", "99-value", returnValue);
}
@Test
public void resolveProvidedArgument() throws Exception {
InvocableHandlerMethod method = invocableHandlerMethod("handle", Integer.class, String.class);
Object returnValue = method.invokeForRequest(webRequest, null, 99, "value");
public void resolveArg() throws Exception {
StubArgumentResolver intResolver = new StubArgumentResolver(Integer.class, 99);
StubArgumentResolver stringResolver = new StubArgumentResolver(String.class, "value");
assertEquals("Expected raw return value with no handlers registered", String.class, returnValue.getClass());
assertEquals("Provided argument values were not resolved", "99-value", returnValue);
HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
composite.addResolver(intResolver);
composite.addResolver(stringResolver);
handleMethod.setHandlerMethodArgumentResolvers(composite);
Object returnValue = handleMethod.invokeForRequest(webRequest, null);
assertEquals(1, intResolver.getResolvedParameters().size());
assertEquals(1, stringResolver.getResolvedParameters().size());
assertEquals("99-value", returnValue);
assertEquals("intArg", intResolver.getResolvedParameters().get(0).getParameterName());
assertEquals("stringArg", stringResolver.getResolvedParameters().get(0).getParameterName());
}
@Test
public void discoverParameterName() throws Exception {
StubArgumentResolver resolver = addResolver(Integer.class, 99);
InvocableHandlerMethod method = invocableHandlerMethod("parameterNameDiscovery", Integer.class);
method.invokeForRequest(webRequest, null);
public void resolveNullArg() throws Exception {
StubArgumentResolver intResolver = new StubArgumentResolver(Integer.class, null);
StubArgumentResolver stringResolver = new StubArgumentResolver(String.class, null);
HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
composite.addResolver(intResolver);
composite.addResolver(stringResolver);
handleMethod.setHandlerMethodArgumentResolvers(composite);
assertEquals("intArg", resolver.getResolvedParameters().get(0).getParameterName());
Object returnValue = handleMethod.invokeForRequest(webRequest, null);
assertEquals(1, intResolver.getResolvedParameters().size());
assertEquals(1, stringResolver.getResolvedParameters().size());
assertEquals("null-null", returnValue);
}
private StubArgumentResolver addResolver(Class<?> parameterType, Object stubValue) {
StubArgumentResolver resolver = new StubArgumentResolver(parameterType, stubValue);
argumentResolvers.addResolver(resolver);
return resolver;
@Test
public void cannotResolveArg() throws Exception {
try {
handleMethod.invokeForRequest(webRequest, null);
fail("Expected exception");
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("No suitable resolver for argument [0] [type=java.lang.Integer]"));
}
}
@Test
public void resolveProvidedArg() throws Exception {
Object returnValue = handleMethod.invokeForRequest(webRequest, null, 99, "value");
assertEquals(String.class, returnValue.getClass());
assertEquals("99-value", returnValue);
}
@Test
public void resolveProvidedArgFirst() throws Exception {
StubArgumentResolver intResolver = new StubArgumentResolver(Integer.class, 1);
StubArgumentResolver stringResolver = new StubArgumentResolver(String.class, "value1");
HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
composite.addResolver(intResolver);
composite.addResolver(stringResolver);
handleMethod.setHandlerMethodArgumentResolvers(composite);
Object returnValue = handleMethod.invokeForRequest(webRequest, null, 2, "value2");
assertEquals("2-value2", returnValue);
}
private InvocableHandlerMethod invocableHandlerMethod(String methodName, Class<?>... paramTypes)
throws Exception {
Method method = Handler.class.getDeclaredMethod(methodName, paramTypes);
InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(new Handler(), method);
handlerMethod.setHandlerMethodArgumentResolvers(argumentResolvers);
return handlerMethod;
@Test
public void exceptionInResolvingArg() throws Exception {
HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
composite.addResolver(new ExceptionRaisingArgumentResolver());
handleMethod.setHandlerMethodArgumentResolvers(composite);
try {
handleMethod.invokeForRequest(webRequest, null);
fail("Expected exception");
} catch (HttpMessageNotReadableException ex) {
// Expected..
// Allow HandlerMethodArgumentResolver exceptions to propagate..
}
}
@Test
public void illegalArgumentException() throws Exception {
StubArgumentResolver intResolver = new StubArgumentResolver(Integer.class, "__invalid__");
StubArgumentResolver stringResolver = new StubArgumentResolver(String.class, "value");
HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
composite.addResolver(intResolver);
composite.addResolver(stringResolver);
handleMethod.setHandlerMethodArgumentResolvers(composite);
try {
handleMethod.invokeForRequest(webRequest, null);
fail("Expected exception");
} catch (IllegalArgumentException ex) {
assertNotNull("Exception not wrapped", ex.getCause());
assertTrue(ex.getCause() instanceof IllegalArgumentException);
assertTrue(ex.getMessage().contains("Controller ["));
assertTrue(ex.getMessage().contains("Method ["));
assertTrue(ex.getMessage().contains("Resolved arguments: "));
assertTrue(ex.getMessage().contains("[0] [type=java.lang.String] [value=__invalid__]"));
assertTrue(ex.getMessage().contains("[1] [type=java.lang.String] [value=value"));
}
}
@Test
public void invocationTargetException() throws Exception {
Throwable expected = new RuntimeException("error");
try {
invokeExceptionRaisingHandler(expected);
} catch (RuntimeException actual) {
assertSame(expected, actual);
}
expected = new Error("error");
try {
invokeExceptionRaisingHandler(expected);
} catch (Error actual) {
assertSame(expected, actual);
}
expected = new Exception("error");
try {
invokeExceptionRaisingHandler(expected);
} catch (Exception actual) {
assertSame(expected, actual);
}
expected = new Throwable("error");
try {
invokeExceptionRaisingHandler(expected);
} catch (IllegalStateException actual) {
assertNotNull(actual.getCause());
assertSame(expected, actual.getCause());
assertTrue(actual.getMessage().contains("Failed to invoke controller method"));
}
}
private void invokeExceptionRaisingHandler(Throwable expected) throws Exception {
Method method = ExceptionRaisingHandler.class.getDeclaredMethod("raiseException");
Object handler = new ExceptionRaisingHandler(expected);
new InvocableHandlerMethod(handler, method).invokeForRequest(webRequest, null);
fail("Expected exception");
}
@SuppressWarnings("unused")
private static class Handler {
@SuppressWarnings("unused")
public String handle(Integer intArg, String stringArg) {
return intArg + "-" + stringArg;
}
@SuppressWarnings("unused")
@RequestMapping
public void parameterNameDiscovery(Integer intArg) {
}
}
@SuppressWarnings("unused")
private static class ExceptionRaisingHandler {
private final Throwable t;
public ExceptionRaisingHandler(Throwable t) {
this.t = t;
}
public void raiseException() throws Throwable {
throw t;
}
}
private static class ExceptionRaisingArgumentResolver implements HandlerMethodArgumentResolver {
public boolean supportsParameter(MethodParameter parameter) {
return true;
}
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
throw new HttpMessageNotReadableException("oops, can't read");
}
}
}

View File

@@ -24,7 +24,8 @@ import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
/**
* Resolves a method argument using a stub value and records resolved parameters.
* Supports parameters of a given type and resolves them using a stub value.
* Also records the resolved parameter value.
*
* @author Rossen Stoyanchev
*/
@@ -36,8 +37,8 @@ public class StubArgumentResolver implements HandlerMethodArgumentResolver {
private List<MethodParameter> resolvedParameters = new ArrayList<MethodParameter>();
public StubArgumentResolver(Class<?> parameterType, Object stubValue) {
this.parameterType = parameterType;
public StubArgumentResolver(Class<?> supportedParameterType, Object stubValue) {
this.parameterType = supportedParameterType;
this.stubValue = stubValue;
}