diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java index c2e030e9e6..3375322962 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -33,7 +33,6 @@ import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; -import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.converter.ByteArrayHttpMessageConverter; @@ -57,17 +56,17 @@ import org.springframework.web.context.request.WebRequest; import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethodSelector; import org.springframework.web.method.annotation.ErrorsMethodArgumentResolver; +import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver; import org.springframework.web.method.annotation.MapMethodProcessor; import org.springframework.web.method.annotation.ModelAttributeMethodProcessor; import org.springframework.web.method.annotation.ModelFactory; import org.springframework.web.method.annotation.ModelMethodProcessor; import org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver; +import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver; import org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver; +import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver; import org.springframework.web.method.annotation.SessionAttributesHandler; import org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver; -import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver; -import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver; -import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; @@ -84,9 +83,9 @@ import org.springframework.web.util.WebUtils; /** * An {@link AbstractHandlerMethodAdapter} that supports {@link HandlerMethod}s - * with the signature -- method argument and return types, defined in + * with the signature -- method argument and return types, defined in * {@code @RequestMapping}. - * + * *

Support for custom argument and return value types can be added via * {@link #setCustomArgumentResolvers} and {@link #setCustomReturnValueHandlers}. * Or alternatively to re-configure all argument and return value types use @@ -103,7 +102,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i private List customArgumentResolvers; private List customReturnValueHandlers; - + private List modelAndViewResolvers; private List> messageConverters; @@ -115,20 +114,20 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i private boolean synchronizeOnSession = false; private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); - + private ConfigurableBeanFactory beanFactory; private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore(); - + private boolean ignoreDefaultModelOnRedirect = false; - + private final Map, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap, SessionAttributesHandler>(); private HandlerMethodArgumentResolverComposite argumentResolvers; private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers; - + private HandlerMethodReturnValueHandlerComposite returnValueHandlers; private final Map, Set> dataBinderFactoryCache = new ConcurrentHashMap, Set>(); @@ -139,7 +138,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i * Default constructor. */ public RequestMappingHandlerAdapter() { - + StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); stringHttpMessageConverter.setWriteAcceptCharset(false); // See SPR-7316 @@ -152,7 +151,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i /** * Provide resolvers for custom argument types. Custom resolvers are ordered - * after built-in ones. To override the built-in support for argument + * after built-in ones. To override the built-in support for argument * resolution use {@link #setArgumentResolvers} instead. */ public void setCustomArgumentResolvers(List argumentResolvers) { @@ -179,9 +178,9 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i this.argumentResolvers.addResolvers(argumentResolvers); } } - + /** - * Return the configured argument resolvers, or possibly {@code null} if + * Return the configured argument resolvers, or possibly {@code null} if * not initialized yet via {@link #afterPropertiesSet()}. */ public HandlerMethodArgumentResolverComposite getArgumentResolvers() { @@ -226,7 +225,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } /** - * Configure the complete list of supported return value types thus + * Configure the complete list of supported return value types thus * overriding handlers that would otherwise be configured by default. */ public void setReturnValueHandlers(List returnValueHandlers) { @@ -240,7 +239,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } /** - * Return the configured handlers, or possibly {@code null} if not + * Return the configured handlers, or possibly {@code null} if not * initialized yet via {@link #afterPropertiesSet()}. */ public HandlerMethodReturnValueHandlerComposite getReturnValueHandlers() { @@ -248,16 +247,16 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } /** - * Provide custom {@link ModelAndViewResolver}s. - *

Note: This method is available for backwards - * compatibility only. However, it is recommended to re-write a + * Provide custom {@link ModelAndViewResolver}s. + *

Note: This method is available for backwards + * compatibility only. However, it is recommended to re-write a * {@code ModelAndViewResolver} as {@link HandlerMethodReturnValueHandler}. - * An adapter between the two interfaces is not possible since the + * An adapter between the two interfaces is not possible since the * {@link HandlerMethodReturnValueHandler#supportsReturnType} method * cannot be implemented. Hence {@code ModelAndViewResolver}s are limited - * to always being invoked at the end after all other return value + * to always being invoked at the end after all other return value * handlers have been given a chance. - *

A {@code HandlerMethodReturnValueHandler} provides better access to + *

A {@code HandlerMethodReturnValueHandler} provides better access to * the return type and controller method information and can be ordered * freely relative to other return value handlers. */ @@ -273,8 +272,8 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } /** - * Provide the converters to use in argument resolvers and return value - * handlers that support reading and/or writing to the body of the + * Provide the converters to use in argument resolvers and return value + * handlers that support reading and/or writing to the body of the * request and response. */ public void setMessageConverters(List> messageConverters) { @@ -289,7 +288,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } /** - * Provide a WebBindingInitializer with "global" initialization to apply + * Provide a WebBindingInitializer with "global" initialization to apply * to every DataBinder instance. */ public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) { @@ -304,20 +303,20 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } /** - * Specify the strategy to store session attributes with. The default is + * Specify the strategy to store session attributes with. The default is * {@link org.springframework.web.bind.support.DefaultSessionAttributeStore}, - * storing session attributes in the HttpSession with the same attribute + * storing session attributes in the HttpSession with the same attribute * name as in the model. */ public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) { this.sessionAttributeStore = sessionAttributeStore; } - + /** * Cache content produced by @SessionAttributes annotated handlers * for the given number of seconds. Default is 0, preventing caching completely. - *

In contrast to the "cacheSeconds" property which will apply to all general - * handlers (but not to @SessionAttributes annotated handlers), + *

In contrast to the "cacheSeconds" property which will apply to all general + * handlers (but not to @SessionAttributes annotated handlers), * this setting will apply to @SessionAttributes handlers only. * @see #setCacheSeconds * @see org.springframework.web.bind.annotation.SessionAttributes @@ -349,25 +348,25 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } /** - * Set the ParameterNameDiscoverer to use for resolving method parameter - * names if needed (e.g. for default attribute names). Default is a + * Set the ParameterNameDiscoverer to use for resolving method parameter + * names if needed (e.g. for default attribute names). Default is a * {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}. */ public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { this.parameterNameDiscoverer = parameterNameDiscoverer; } - + /** - * By default the content of the "default" model is used both during - * rendering and redirect scenarios. Alternatively a controller method + * By default the content of the "default" model is used both during + * rendering and redirect scenarios. Alternatively a controller method * can declare a {@link RedirectAttributes} argument and use it to provide * attributes for a redirect. - *

Setting this flag to {@code true} guarantees the "default" model is - * never used in a redirect scenario even if a RedirectAttributes argument - * is not declared. Setting it to {@code false} means the "default" model - * may be used in a redirect if the controller method doesn't declare a + *

Setting this flag to {@code true} guarantees the "default" model is + * never used in a redirect scenario even if a RedirectAttributes argument + * is not declared. Setting it to {@code false} means the "default" model + * may be used in a redirect if the controller method doesn't declare a * RedirectAttributes argument. - *

The default setting is {@code false} but new applications should + *

The default setting is {@code false} but new applications should * consider setting it to {@code true}. * @see RedirectAttributes */ @@ -377,7 +376,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i /** * {@inheritDoc} - *

A {@link ConfigurableBeanFactory} is expected for resolving + *

A {@link ConfigurableBeanFactory} is expected for resolving * expressions in method argument default values. */ public void setBeanFactory(BeanFactory beanFactory) { @@ -387,7 +386,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } /** - * Return the owning factory of this bean instance, or {@code null}. + * Return the owning factory of this bean instance, or {@code null}. */ protected ConfigurableBeanFactory getBeanFactory() { return this.beanFactory; @@ -451,7 +450,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } /** - * Return the list of argument resolvers to use for {@code @InitBinder} + * Return the list of argument resolvers to use for {@code @InitBinder} * methods including built-in and custom resolvers. */ private List getDefaultInitBinderArgumentResolvers() { @@ -474,12 +473,12 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i // Catch-all resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); - + return resolvers; } /** - * Return the list of return value handlers to use including built-in and + * Return the list of return value handlers to use including built-in and * custom handlers provided via {@link #setReturnValueHandlers}. */ private List getDefaultReturnValueHandlers() { @@ -516,33 +515,22 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } /** - * Return {@code true} if all arguments and the return value of the given - * HandlerMethod are supported by the configured resolvers and handlers. + * Always return {@code true} since any method argument and return value + * type will be processed in some way. A method argument not recognized + * by any HandlerMethodArgumentResolver is interpreted as a request parameter + * if it is a simple type, or as a model attribute otherwise. A return value + * not recognized by any HandlerMethodReturnValueHandler will be interpreted + * as a model attribute. */ @Override protected boolean supportsInternal(HandlerMethod handlerMethod) { - return supportsMethodParameters(handlerMethod.getMethodParameters()) && - supportsReturnType(handlerMethod.getReturnType()); - } - - private boolean supportsMethodParameters(MethodParameter[] methodParameters) { - for (MethodParameter methodParameter : methodParameters) { - if (! this.argumentResolvers.supportsParameter(methodParameter)) { - return false; - } - } return true; } - private boolean supportsReturnType(MethodParameter methodReturnType) { - return (this.returnValueHandlers.supportsReturnType(methodReturnType) || - Void.TYPE.equals(methodReturnType.getParameterType())); - } - /** - * This implementation always returns -1. An {@code @RequestMapping} - * method can calculate the lastModified value, call - * {@link WebRequest#checkNotModified(long)}, and return {@code null} + * This implementation always returns -1. An {@code @RequestMapping} + * method can calculate the lastModified value, call + * {@link WebRequest#checkNotModified(long)}, and return {@code null} * if the result of that call is {@code true}. */ @Override @@ -552,8 +540,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i @Override protected final ModelAndView handleInternal(HttpServletRequest request, - HttpServletResponse response, - HandlerMethod handlerMethod) throws Exception { + HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { // Always prevent caching in case of session attribute management. @@ -563,7 +550,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i // Uses configured default cacheSeconds setting. checkAndPrepare(request, response, true); } - + // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); @@ -574,12 +561,12 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } } } - + return invokeHandlerMethod(request, response, handlerMethod); } /** - * Return the {@link SessionAttributesHandler} instance for the given + * Return the {@link SessionAttributesHandler} instance for the given * handler type, never {@code null}. */ private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) { @@ -602,7 +589,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i */ private ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { - + ServletWebRequest webRequest = new ServletWebRequest(request, response); WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); @@ -634,17 +621,17 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i } } - private ServletInvocableHandlerMethod createRequestMappingMethod(HandlerMethod handlerMethod, + private ServletInvocableHandlerMethod createRequestMappingMethod(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) { ServletInvocableHandlerMethod requestMethod; - requestMethod = new ServletInvocableHandlerMethod(handlerMethod.getBean(), handlerMethod.getMethod()); + requestMethod = new ServletInvocableHandlerMethod(handlerMethod); requestMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); requestMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); requestMethod.setDataBinderFactory(binderFactory); requestMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); return requestMethod; } - + private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) { SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod); Class handlerType = handlerMethod.getBeanType(); @@ -692,6 +679,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i */ protected ServletRequestDataBinderFactory createDataBinderFactory(List binderMethods) throws Exception { + return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer()); } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java index 3eb3261fb7..9c7d381e7d 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java @@ -22,9 +22,8 @@ import java.lang.reflect.Method; import org.springframework.http.HttpStatus; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; -import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite; import org.springframework.web.method.support.InvocableHandlerMethod; @@ -32,16 +31,19 @@ import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.View; /** - * Extends {@link InvocableHandlerMethod} with the ability to handle the value returned from the method through - * a registered {@link HandlerMethodArgumentResolver} that supports the given return value type. - * Return value handling may include writing to the response or updating the {@link ModelAndViewContainer} structure. + * Extends {@link InvocableHandlerMethod} with the ability to handle return + * values through a registered {@link HandlerMethodReturnValueHandler} and + * also supports setting the response status based on a method-level + * {@code @ResponseStatus} annotation. * - *

If the underlying method has a {@link ResponseStatus} instruction, the status on the response is set - * accordingly after the method is invoked but before the return value is handled. + *

A {@code null} return value (including void) may be interpreted as the + * end of request processing in combination with a {@code @ResponseStatus} + * annotation, a not-modified check condition + * (see {@link ServletWebRequest#checkNotModified(long)}), or + * a method argument that provides access to the response stream. * * @author Rossen Stoyanchev * @since 3.1 - * @see #invokeAndHandle(NativeWebRequest, ModelAndViewContainer, Object...) */ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { @@ -51,64 +53,71 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { private HandlerMethodReturnValueHandlerComposite returnValueHandlers; + + /** + * Creates an instance from the given handler and method. + */ + public ServletInvocableHandlerMethod(Object handler, Method method) { + super(handler, method); + initResponseStatus(); + } + + /** + * Create an instance from a {@code HandlerMethod}. + */ + public ServletInvocableHandlerMethod(HandlerMethod handlerMethod) { + super(handlerMethod); + initResponseStatus(); + } + + private void initResponseStatus() { + ResponseStatus annot = getMethodAnnotation(ResponseStatus.class); + if (annot != null) { + this.responseStatus = annot.value(); + this.responseReason = annot.reason(); + } + } + + /** + * Register {@link HandlerMethodReturnValueHandler} instances to use to + * handle return values. + */ public void setHandlerMethodReturnValueHandlers(HandlerMethodReturnValueHandlerComposite returnValueHandlers) { this.returnValueHandlers = returnValueHandlers; } /** - * Creates a {@link ServletInvocableHandlerMethod} instance with the given bean and method. - * @param handler the object handler - * @param method the method - */ - public ServletInvocableHandlerMethod(Object handler, Method method) { - super(handler, method); - - ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class); - if (annotation != null) { - this.responseStatus = annotation.value(); - this.responseReason = annotation.reason(); - } - } - - /** - * Invokes the method and handles the return value through a registered {@link HandlerMethodReturnValueHandler}. - *

Return value handling may be skipped entirely when the method returns {@code null} (also possibly due - * to a {@code void} return type) and one of the following additional conditions is true: - *

- *

After the return value is handled, callers of this method can use the {@link ModelAndViewContainer} - * to gain access to model attributes, view selection choices, and to check if view resolution is even needed. + * Invokes the method and handles the return value through a registered + * {@link HandlerMethodReturnValueHandler}. * - * @param request the current request - * @param mavContainer the {@link ModelAndViewContainer} for the current request - * @param providedArgs argument values to try to use without the need for view resolution + * @param webRequest the current request + * @param mavContainer the ModelAndViewContainer for this request + * @param providedArgs "given" arguments matched by type, not resolved */ - public final void invokeAndHandle( - NativeWebRequest request, ModelAndViewContainer mavContainer, - Object... providedArgs) throws Exception { + public final void invokeAndHandle(ServletWebRequest webRequest, + ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { - Object returnValue = invokeForRequest(request, mavContainer, providedArgs); + Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); - setResponseStatus((ServletWebRequest) request); + setResponseStatus(webRequest); if (returnValue == null) { - if (isRequestNotModified(request) || hasResponseStatus() || mavContainer.isRequestHandled()) { + if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) { mavContainer.setRequestHandled(true); return; } } + else if (StringUtils.hasText(this.responseReason)) { + mavContainer.setRequestHandled(true); + return; + } mavContainer.setRequestHandled(false); try { - returnValueHandlers.handleReturnValue(returnValue, getReturnType(), mavContainer, request); - } catch (Exception ex) { + this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest); + } + catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex); } @@ -116,6 +125,41 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { } } + /** + * Set the response status according to the {@link ResponseStatus} annotation. + */ + private void setResponseStatus(ServletWebRequest webRequest) throws IOException { + if (this.responseStatus == null) { + return; + } + + if (StringUtils.hasText(this.responseReason)) { + webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason); + } + else { + webRequest.getResponse().setStatus(this.responseStatus.value()); + } + + // to be picked up by the RedirectView + webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus); + } + + /** + * Does the given request qualify as "not modified"? + * @see ServletWebRequest#checkNotModified(long) + * @see ServletWebRequest#checkNotModified(String) + */ + private boolean isRequestNotModified(ServletWebRequest webRequest) { + return webRequest.isNotModified(); + } + + /** + * Does this method have the response status instruction? + */ + private boolean hasResponseStatus() { + return responseStatus != null; + } + private String getReturnValueHandlingErrorMessage(String message, Object returnValue) { StringBuilder sb = new StringBuilder(message); if (returnValue != null) { @@ -125,36 +169,4 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { return getDetailedErrorMessage(sb.toString()); } - /** - * Set the response status according to the {@link ResponseStatus} annotation. - */ - private void setResponseStatus(ServletWebRequest webRequest) throws IOException { - if (this.responseStatus != null) { - if (StringUtils.hasText(this.responseReason)) { - webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason); - } - else { - webRequest.getResponse().setStatus(this.responseStatus.value()); - } - - // to be picked up by the RedirectView - webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus); - } - } - - /** - * Does the given request qualify as "not modified"? - * @see ServletWebRequest#checkNotModified(long) - * @see ServletWebRequest#checkNotModified(String) - */ - private boolean isRequestNotModified(NativeWebRequest request) { - return ((ServletWebRequest) request).isNotModified(); - } - - /** - * Does this method have the response status instruction? - */ - private boolean hasResponseStatus() { - return responseStatus != null; - } } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java index 00a7a3abf5..f92e8478fc 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -15,9 +15,10 @@ */ package org.springframework.web.servlet.mvc.method.annotation; -import static org.junit.Assert.*; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.lang.reflect.Method; @@ -30,17 +31,20 @@ import org.springframework.http.HttpStatus; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite; import org.springframework.web.method.support.ModelAndViewContainer; +import org.springframework.web.servlet.view.RedirectView; /** * Test fixture with {@link ServletInvocableHandlerMethod}. - * + * * @author Rossen Stoyanchev */ public class ServletInvocableHandlerMethodTests { @@ -53,6 +57,8 @@ public class ServletInvocableHandlerMethodTests { private ServletWebRequest webRequest; + private MockHttpServletRequest request; + private MockHttpServletResponse response; @Before @@ -60,24 +66,23 @@ public class ServletInvocableHandlerMethodTests { returnValueHandlers = new HandlerMethodReturnValueHandlerComposite(); argumentResolvers = new HandlerMethodArgumentResolverComposite(); mavContainer = new ModelAndViewContainer(); + request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); - webRequest = new ServletWebRequest(new MockHttpServletRequest(), response); + webRequest = new ServletWebRequest(request, response); } @Test - public void nullReturnValueResponseStatus() throws Exception { + public void invokeAndHandle_VoidWithResponseStatus() throws Exception { ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("responseStatus"); handlerMethod.invokeAndHandle(webRequest, mavContainer); assertTrue("Null return value + @ResponseStatus should result in 'request handled'", mavContainer.isRequestHandled()); - assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus()); - assertEquals("400 Bad Request", response.getErrorMessage()); } @Test - public void nullReturnValueHttpServletResponseArg() throws Exception { + public void invokeAndHandle_VoidWithHttpServletResponseArgument() throws Exception { argumentResolvers.addResolver(new ServletResponseMethodArgumentResolver()); ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("httpServletResponse", HttpServletResponse.class); @@ -88,33 +93,61 @@ public class ServletInvocableHandlerMethodTests { } @Test - public void nullReturnValueRequestNotModified() throws Exception { + public void invokeAndHandle_VoidRequestNotModified() throws Exception { webRequest.getNativeRequest(MockHttpServletRequest.class).addHeader("If-Modified-Since", 10 * 1000 * 1000); int lastModifiedTimestamp = 1000 * 1000; webRequest.checkNotModified(lastModifiedTimestamp); - + ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("notModified"); handlerMethod.invokeAndHandle(webRequest, mavContainer); assertTrue("Null return value + 'not modified' request should result in 'request handled'", mavContainer.isRequestHandled()); } - + + // SPR-9159 + @Test - public void exceptionWhileHandlingReturnValue() throws Exception { + public void invokeAndHandle_NotVoidWithResponseStatusAndReason() throws Exception { + ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("responseStatusWithReason"); + handlerMethod.invokeAndHandle(webRequest, mavContainer); + + assertTrue("When a phrase is used, the response should not be used any more", mavContainer.isRequestHandled()); + assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus()); + assertEquals("400 Bad Request", response.getErrorMessage()); + } + + @Test(expected=HttpMessageNotWritableException.class) + public void invokeAndHandle_Exception() throws Exception { returnValueHandlers.addHandler(new ExceptionRaisingReturnValueHandler()); ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("handle"); - try { - handlerMethod.invokeAndHandle(webRequest, mavContainer); - fail("Expected exception"); - } catch (HttpMessageNotWritableException ex) { - // Expected.. - // Allow HandlerMethodArgumentResolver exceptions to propagate.. - } + handlerMethod.invokeAndHandle(webRequest, mavContainer); + fail("Expected exception"); } - private ServletInvocableHandlerMethod getHandlerMethod(String methodName, Class... argTypes) + @Test + public void invokeAndHandle_DynamicReturnValue() throws Exception { + argumentResolvers.addResolver(new RequestParamMethodArgumentResolver(null, false)); + returnValueHandlers.addHandler(new ViewMethodReturnValueHandler()); + returnValueHandlers.addHandler(new ViewNameMethodReturnValueHandler()); + + // Invoke without a request parameter (String return value) + ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("dynamicReturnValue", String.class); + handlerMethod.invokeAndHandle(webRequest, mavContainer); + + assertNotNull(mavContainer.getView()); + assertEquals(RedirectView.class, mavContainer.getView().getClass()); + + // Invoke with a request parameter (RedirectView return value) + request.setParameter("param", "value"); + handlerMethod.invokeAndHandle(webRequest, mavContainer); + + assertEquals("view", mavContainer.getViewName()); + } + + + private ServletInvocableHandlerMethod getHandlerMethod(String methodName, Class... argTypes) throws NoSuchMethodException { Method method = Handler.class.getDeclaredMethod(methodName, argTypes); ServletInvocableHandlerMethod handlerMethod = new ServletInvocableHandlerMethod(new Handler(), method); @@ -130,16 +163,24 @@ public class ServletInvocableHandlerMethodTests { return "view"; } - @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "400 Bad Request") + @ResponseStatus(value = HttpStatus.BAD_REQUEST) public void responseStatus() { } - + + @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "400 Bad Request") + public String responseStatusWithReason() { + return "foo"; + } + public void httpServletResponse(HttpServletResponse response) { } - + public void notModified() { } - + + public Object dynamicReturnValue(@RequestParam(required=false) String param) { + return (param != null) ? "view" : new RedirectView("redirectView"); + } } private static class ExceptionRaisingReturnValueHandler implements HandlerMethodReturnValueHandler { diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/HandlerMethod.java b/org.springframework.web/src/main/java/org/springframework/web/method/HandlerMethod.java index cdf3f5f845..9ed46d9cf2 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/HandlerMethod.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/HandlerMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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,13 +29,15 @@ import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** - * Encapsulates information about a bean method consisting of a {@linkplain #getMethod() method} and a - * {@linkplain #getBean() bean}. Provides convenient access to method parameters, the method return value, - * method annotations. + * Encapsulates information about a bean method consisting of a + * {@linkplain #getMethod() method} and a {@linkplain #getBean() bean}. Provides + * convenient access to method parameters, the method return value, method + * annotations. * - *

The class may be created with a bean instance or with a bean name (e.g. lazy bean, prototype bean). - * Use {@link #createWithResolvedBean()} to obtain an {@link HandlerMethod} instance with a bean instance - * initialized through the bean factory. + *

The class may be created with a bean instance or with a bean name (e.g. lazy + * bean, prototype bean). Use {@link #createWithResolvedBean()} to obtain an + * {@link HandlerMethod} instance with a bean instance initialized through the + * bean factory. * * @author Arjen Poutsma * @author Rossen Stoyanchev @@ -44,26 +46,25 @@ import org.springframework.util.ClassUtils; public class HandlerMethod { /** Logger that is available to subclasses */ - protected final Log logger = LogFactory.getLog(getClass()); + protected final Log logger = LogFactory.getLog(HandlerMethod.class); private final Object bean; private final Method method; - + private final BeanFactory beanFactory; private MethodParameter[] parameters; private final Method bridgedMethod; + /** - * Constructs a new handler method with the given bean instance and method. - * @param bean the object bean - * @param method the method + * Create an instance from a bean instance and a method. */ public HandlerMethod(Object bean, Method method) { - Assert.notNull(bean, "bean must not be null"); - Assert.notNull(method, "method must not be null"); + Assert.notNull(bean, "bean is required"); + Assert.notNull(method, "method is required"); this.bean = bean; this.beanFactory = null; this.method = method; @@ -71,15 +72,12 @@ public class HandlerMethod { } /** - * Constructs a new handler method with the given bean instance, method name and parameters. - * @param bean the object bean - * @param methodName the method name - * @param parameterTypes the method parameter types + * Create an instance from a bean instance, method name, and parameter types. * @throws NoSuchMethodException when the method cannot be found */ public HandlerMethod(Object bean, String methodName, Class... parameterTypes) throws NoSuchMethodException { - Assert.notNull(bean, "bean must not be null"); - Assert.notNull(methodName, "method must not be null"); + Assert.notNull(bean, "bean is required"); + Assert.notNull(methodName, "method is required"); this.bean = bean; this.beanFactory = null; this.method = bean.getClass().getMethod(methodName, parameterTypes); @@ -87,24 +85,34 @@ public class HandlerMethod { } /** - * Constructs a new handler method with the given bean name and method. The bean name will be lazily - * initialized when {@link #createWithResolvedBean()} is called. - * @param beanName the bean name - * @param beanFactory the bean factory to use for bean initialization - * @param method the method for the bean + * Create an instance from a bean name, a method, and a {@code BeanFactory}. + * The method {@link #createWithResolvedBean()} may be used later to + * re-create the {@code HandlerMethod} with an initialized the bean. */ public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) { - Assert.hasText(beanName, "'beanName' must not be null"); - Assert.notNull(beanFactory, "'beanFactory' must not be null"); - Assert.notNull(method, "'method' must not be null"); + Assert.hasText(beanName, "beanName is required"); + Assert.notNull(beanFactory, "beanFactory is required"); + Assert.notNull(method, "method is required"); Assert.isTrue(beanFactory.containsBean(beanName), - "Bean factory [" + beanFactory + "] does not contain bean " + "with name [" + beanName + "]"); + "Bean factory [" + beanFactory + "] does not contain bean [" + beanName + "]"); this.bean = beanName; this.beanFactory = beanFactory; this.method = method; this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); } + /** + * Create an instance from another {@code HandlerMethod}. + */ + protected HandlerMethod(HandlerMethod handlerMethod) { + Assert.notNull(handlerMethod, "HandlerMethod is required"); + this.bean = handlerMethod.bean; + this.beanFactory = handlerMethod.beanFactory; + this.method = handlerMethod.method; + this.bridgedMethod = handlerMethod.bridgedMethod; + this.parameters = handlerMethod.parameters; + } + /** * Returns the bean for this handler method. */ @@ -120,25 +128,22 @@ public class HandlerMethod { } /** - * Returns the type of the handler for this handler method. + * Returns the type of the handler for this handler method. * Note that if the bean type is a CGLIB-generated class, the original, user-defined class is returned. */ public Class getBeanType() { - if (bean instanceof String) { - String beanName = (String) bean; - return beanFactory.getType(beanName); - } - else { - return ClassUtils.getUserClass(bean.getClass()); - } + Class clazz = (this.bean instanceof String) + ? this.beanFactory.getType((String) this.bean) : this.bean.getClass(); + + return ClassUtils.getUserClass(clazz); } - + /** * If the bean method is a bridge method, this method returns the bridged (user-defined) method. * Otherwise it returns the same method as {@link #getMethod()}. */ protected Method getBridgedMethod() { - return bridgedMethod; + return this.bridgedMethod; } /** @@ -147,20 +152,26 @@ public class HandlerMethod { public MethodParameter[] getMethodParameters() { if (this.parameters == null) { int parameterCount = this.bridgedMethod.getParameterTypes().length; - MethodParameter[] p = new MethodParameter[parameterCount]; + this.parameters = new MethodParameter[parameterCount]; for (int i = 0; i < parameterCount; i++) { - p[i] = new HandlerMethodParameter(this.bridgedMethod, i); + this.parameters[i] = new HandlerMethodParameter(i); } - this.parameters = p; } - return parameters; + return this.parameters; } /** - * Returns the method return type, as {@code MethodParameter}. + * Return the HandlerMethod return type. */ public MethodParameter getReturnType() { - return new HandlerMethodParameter(this.bridgedMethod, -1); + return new HandlerMethodParameter(-1); + } + + /** + * Return the actual return value type. + */ + public MethodParameter getReturnValueType(Object returnValue) { + return new ReturnValueMethodParameter(returnValue); } /** @@ -171,8 +182,8 @@ public class HandlerMethod { } /** - * Returns a single annotation on the underlying method traversing its super methods if no - * annotation can be found on the given method itself. + * Returns a single annotation on the underlying method traversing its super methods if no + * annotation can be found on the given method itself. * @param annotationType the type of annotation to introspect the method for. * @return the annotation, or {@code null} if none found */ @@ -181,7 +192,7 @@ public class HandlerMethod { } /** - * If the provided instance contains a bean name rather than an object instance, the bean name is resolved + * If the provided instance contains a bean name rather than an object instance, the bean name is resolved * before a {@link HandlerMethod} is created and returned. */ public HandlerMethod createWithResolvedBean() { @@ -190,9 +201,11 @@ public class HandlerMethod { String beanName = (String) this.bean; handler = this.beanFactory.getBean(beanName); } - return new HandlerMethod(handler, method); + HandlerMethod handlerMethod = new HandlerMethod(handler, this.method); + handlerMethod.parameters = getMethodParameters(); + return handlerMethod; } - + @Override public boolean equals(Object o) { if (this == o) { @@ -216,33 +229,41 @@ public class HandlerMethod { } /** - * A {@link MethodParameter} that resolves method annotations even when the actual annotations - * are on a bridge method rather than on the current method. Annotations on super types are - * also returned via {@link AnnotationUtils#findAnnotation(Method, Class)}. + * A MethodParameter with HandlerMethod-specific behavior. */ private class HandlerMethodParameter extends MethodParameter { - - public HandlerMethodParameter(Method method, int parameterIndex) { - super(method, parameterIndex); + + protected HandlerMethodParameter(int index) { + super(HandlerMethod.this.bridgedMethod, index); } - /** - * Return {@link HandlerMethod#getBeanType()} rather than the method's class, which could be - * important for the proper discovery of generic types. - */ @Override public Class getDeclaringClass() { return HandlerMethod.this.getBeanType(); } - /** - * Return the method annotation via {@link HandlerMethod#getMethodAnnotation(Class)}, which will find - * the annotation by traversing super-types and handling annotations on bridge methods correctly. - */ @Override public T getMethodAnnotation(Class annotationType) { return HandlerMethod.this.getMethodAnnotation(annotationType); } } + /** + * A MethodParameter for a HandlerMethod return type based on an actual return value. + */ + private class ReturnValueMethodParameter extends HandlerMethodParameter { + + private final Object returnValue; + + public ReturnValueMethodParameter(Object returnValue) { + super(-1); + this.returnValue = returnValue; + } + + @Override + public Class getParameterType() { + return (this.returnValue != null) ? this.returnValue.getClass() : super.getParameterType(); + } + } + } diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java b/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java index f06d458cbd..fb364be2b3 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java @@ -19,8 +19,6 @@ package org.springframework.web.method.support; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -42,9 +40,6 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe private final List returnValueHandlers = new ArrayList(); - private final Map returnValueHandlerCache = - new ConcurrentHashMap(); - /** * Return a read-only list with the registered handlers, or an empty list. */ @@ -78,21 +73,16 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe * Find a registered {@link HandlerMethodReturnValueHandler} that supports the given return type. */ private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) { - HandlerMethodReturnValueHandler result = this.returnValueHandlerCache.get(returnType); - if (result == null) { - for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) { - if (logger.isTraceEnabled()) { - logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" + - returnType.getGenericParameterType() + "]"); - } - if (returnValueHandler.supportsReturnType(returnType)) { - result = returnValueHandler; - this.returnValueHandlerCache.put(returnType, returnValueHandler); - break; - } + for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) { + if (logger.isTraceEnabled()) { + logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" + + returnType.getGenericParameterType() + "]"); + } + if (returnValueHandler.supportsReturnType(returnType)) { + return returnValueHandler; } } - return result; + return null; } /** diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java b/org.springframework.web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java index 1ce366f52a..8ec8dbb858 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java @@ -53,15 +53,21 @@ public class InvocableHandlerMethod extends HandlerMethod { private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); + /** - * Constructs a new handler method with the given bean instance and method. - * @param bean the bean instance - * @param method the method + * Creates an instance from the given handler and method. */ public InvocableHandlerMethod(Object bean, Method method) { super(bean, method); } + /** + * Create an instance from a {@code HandlerMethod}. + */ + public InvocableHandlerMethod(HandlerMethod handlerMethod) { + super(handlerMethod); + } + /** * Constructs a new handler method with the given bean instance, method name and parameters. * @param bean the object bean @@ -106,8 +112,8 @@ public class InvocableHandlerMethod extends HandlerMethod { * a thrown exception instance. Provided argument values are checked before argument resolvers. * * @param request the current request - * @param mavContainer the {@link ModelAndViewContainer} for the current request - * @param providedArgs argument values to try to use without view resolution + * @param mavContainer the ModelAndViewContainer for this request + * @param providedArgs "given" arguments matched by type, not resolved * @return the raw value returned by the invoked method * @exception Exception raised if no suitable argument resolver can be found, or the method raised an exception */ @@ -193,7 +199,7 @@ public class InvocableHandlerMethod extends HandlerMethod { * Attempt to resolve a method parameter from the list of provided argument values. */ private Object resolveProvidedArgument(MethodParameter parameter, Object... providedArgs) { - if (providedArgs == null || parameter.hasParameterAnnotations()) { + if (providedArgs == null) { return null; } for (Object providedArg : providedArgs) { diff --git a/org.springframework.web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java b/org.springframework.web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java index 0d3bafe735..59b7fc411e 100644 --- a/org.springframework.web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java +++ b/org.springframework.web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -36,19 +36,19 @@ import org.springframework.web.context.request.ServletWebRequest; /** * Test fixture for {@link InvocableHandlerMethod} unit tests. - * + * * @author Rossen Stoyanchev */ public class InvocableHandlerMethodTests { - private InvocableHandlerMethod handleMethod; + private InvocableHandlerMethod handlerMethod; private NativeWebRequest webRequest; @Before public void setUp() throws Exception { Method method = Handler.class.getDeclaredMethod("handle", Integer.class, String.class); - this.handleMethod = new InvocableHandlerMethod(new Handler(), method); + this.handlerMethod = new InvocableHandlerMethod(new Handler(), method); this.webRequest = new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse()); } @@ -60,14 +60,14 @@ public class InvocableHandlerMethodTests { HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite(); composite.addResolver(intResolver); composite.addResolver(stringResolver); - handleMethod.setHandlerMethodArgumentResolvers(composite); - - Object returnValue = handleMethod.invokeForRequest(webRequest, null); - + handlerMethod.setHandlerMethodArgumentResolvers(composite); + + Object returnValue = handlerMethod.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()); } @@ -80,10 +80,10 @@ public class InvocableHandlerMethodTests { HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite(); composite.addResolver(intResolver); composite.addResolver(stringResolver); - handleMethod.setHandlerMethodArgumentResolvers(composite); - - Object returnValue = handleMethod.invokeForRequest(webRequest, null); - + handlerMethod.setHandlerMethodArgumentResolvers(composite); + + Object returnValue = handlerMethod.invokeForRequest(webRequest, null); + assertEquals(1, intResolver.getResolvedParameters().size()); assertEquals(1, stringResolver.getResolvedParameters().size()); assertEquals("null-null", returnValue); @@ -92,7 +92,7 @@ public class InvocableHandlerMethodTests { @Test public void cannotResolveArg() throws Exception { try { - handleMethod.invokeForRequest(webRequest, null); + handlerMethod.invokeForRequest(webRequest, null); fail("Expected exception"); } catch (IllegalStateException ex) { assertTrue(ex.getMessage().contains("No suitable resolver for argument [0] [type=java.lang.Integer]")); @@ -101,7 +101,7 @@ public class InvocableHandlerMethodTests { @Test public void resolveProvidedArg() throws Exception { - Object returnValue = handleMethod.invokeForRequest(webRequest, null, 99, "value"); + Object returnValue = handlerMethod.invokeForRequest(webRequest, null, 99, "value"); assertEquals(String.class, returnValue.getClass()); assertEquals("99-value", returnValue); @@ -115,21 +115,21 @@ public class InvocableHandlerMethodTests { HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite(); composite.addResolver(intResolver); composite.addResolver(stringResolver); - handleMethod.setHandlerMethodArgumentResolvers(composite); + handlerMethod.setHandlerMethodArgumentResolvers(composite); - Object returnValue = handleMethod.invokeForRequest(webRequest, null, 2, "value2"); + Object returnValue = handlerMethod.invokeForRequest(webRequest, null, 2, "value2"); assertEquals("2-value2", returnValue); } - + @Test public void exceptionInResolvingArg() throws Exception { HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite(); composite.addResolver(new ExceptionRaisingArgumentResolver()); - handleMethod.setHandlerMethodArgumentResolvers(composite); - + handlerMethod.setHandlerMethodArgumentResolvers(composite); + try { - handleMethod.invokeForRequest(webRequest, null); + handlerMethod.invokeForRequest(webRequest, null); fail("Expected exception"); } catch (HttpMessageNotReadableException ex) { // Expected.. @@ -145,10 +145,10 @@ public class InvocableHandlerMethodTests { HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite(); composite.addResolver(intResolver); composite.addResolver(stringResolver); - handleMethod.setHandlerMethodArgumentResolvers(composite); + handlerMethod.setHandlerMethodArgumentResolvers(composite); try { - handleMethod.invokeForRequest(webRequest, null); + handlerMethod.invokeForRequest(webRequest, null); fail("Expected exception"); } catch (IllegalArgumentException ex) { assertNotNull("Exception not wrapped", ex.getCause()); @@ -200,10 +200,10 @@ public class InvocableHandlerMethodTests { new InvocableHandlerMethod(handler, method).invokeForRequest(webRequest, null); fail("Expected exception"); } - + @SuppressWarnings("unused") private static class Handler { - + public String handle(Integer intArg, String stringArg) { return intArg + "-" + stringArg; } @@ -211,19 +211,19 @@ public class InvocableHandlerMethodTests { @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) { @@ -235,5 +235,5 @@ public class InvocableHandlerMethodTests { throw new HttpMessageNotReadableException("oops, can't read"); } } - + }