diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestParam.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestParam.java index 1f7ea2e9cf1..25a60947e72 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestParam.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/RequestParam.java @@ -23,33 +23,33 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Annotation which indicates that a method parameter should be bound - * to a web request parameter. Supported for {@link RequestMapping} - * annotated handler methods in Servlet and Portlet environments. + * Annotation which indicates that a method parameter should be bound to a web request parameter. Supported for {@link + * RequestMapping} annotated handler methods in Servlet and Portlet environments. * * @author Arjen Poutsma * @author Juergen Hoeller - * @since 2.5 * @see RequestMapping * @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter * @see org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter + * @since 2.5 */ @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequestParam { - /** - * The request parameter to bind to. - */ + /** The request parameter to bind to. */ String value() default ""; /** - * Whether the parameter is required. - *

Default is true, leading to an exception thrown in case - * of the parameter missing in the request. Switch this to false - * if you prefer a null in case of the parameter missing. + * Whether the parameter is required.

Default is true, leading to an exception thrown in case of the + * parameter missing in the request. Switch this to false if you prefer a null in case of + * the parameter missing.

Alternatively, provide a {@link #defaultValue() defaultValue}, which implicitely sets this + * flag to false. */ boolean required() default true; + /** The default value to use as a fallback. Supplying a default value implicitely sets {@link #required()} to false. */ + String defaultValue() default ""; + } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java index 4e61fb2c46f..8e50de38d2e 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java @@ -38,6 +38,7 @@ import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; import org.springframework.web.bind.WebDataBinder; @@ -56,22 +57,20 @@ import org.springframework.web.context.request.WebRequest; import org.springframework.web.multipart.MultipartRequest; /** - * Support class for invoking an annotated handler method. - * Operates on the introspection results of a {@link HandlerMethodResolver} - * for a specific handler type. + * Support class for invoking an annotated handler method. Operates on the introspection results of a {@link + * HandlerMethodResolver} for a specific handler type. * - *

Used by {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter} - * and {@link org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter}. + *

Used by {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter} and {@link + * org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter}. * * @author Juergen Hoeller - * @since 2.5.2 + * @author Arjen Poutsma * @see #invokeHandlerMethod + * @since 2.5.2 */ public class HandlerMethodInvoker { - /** - * We'll create a lot of these objects, so we don't want a new logger every time. - */ + /** We'll create a lot of these objects, so we don't want a new logger every time. */ private static final Log logger = LogFactory.getLog(HandlerMethodInvoker.class); private final HandlerMethodResolver methodResolver; @@ -86,7 +85,6 @@ public class HandlerMethodInvoker { private final SimpleSessionStatus sessionStatus = new SimpleSessionStatus(); - public HandlerMethodInvoker(HandlerMethodResolver methodResolver) { this(methodResolver, null); } @@ -95,10 +93,11 @@ public class HandlerMethodInvoker { this(methodResolver, bindingInitializer, new DefaultSessionAttributeStore(), null); } - public HandlerMethodInvoker( - HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer, - SessionAttributeStore sessionAttributeStore, ParameterNameDiscoverer parameterNameDiscoverer, - WebArgumentResolver... customArgumentResolvers) { + public HandlerMethodInvoker(HandlerMethodResolver methodResolver, + WebBindingInitializer bindingInitializer, + SessionAttributeStore sessionAttributeStore, + ParameterNameDiscoverer parameterNameDiscoverer, + WebArgumentResolver... customArgumentResolvers) { this.methodResolver = methodResolver; this.bindingInitializer = bindingInitializer; @@ -107,10 +106,10 @@ public class HandlerMethodInvoker { this.customArgumentResolvers = customArgumentResolvers; } - - public final Object invokeHandlerMethod( - Method handlerMethod, Object handler, NativeWebRequest webRequest, ExtendedModelMap implicitModel) - throws Exception { + public final Object invokeHandlerMethod(Method handlerMethod, + Object handler, + NativeWebRequest webRequest, + ExtendedModelMap implicitModel) throws Exception { Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod); try { @@ -124,8 +123,10 @@ public class HandlerMethodInvoker { Object attrValue = doInvokeMethod(attributeMethodToInvoke, handler, args); String attrName = AnnotationUtils.findAnnotation(attributeMethodToInvoke, ModelAttribute.class).value(); if ("".equals(attrName)) { - Class resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass()); - attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue); + Class resolvedType = + GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass()); + attrName = + Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue); } implicitModel.addAttribute(attrName, attrValue); } @@ -142,9 +143,10 @@ public class HandlerMethodInvoker { } @SuppressWarnings("unchecked") - private Object[] resolveHandlerArguments( - Method handlerMethod, Object handler, NativeWebRequest webRequest, ExtendedModelMap implicitModel) - throws Exception { + private Object[] resolveHandlerArguments(Method handlerMethod, + Object handler, + NativeWebRequest webRequest, + ExtendedModelMap implicitModel) throws Exception { Class[] paramTypes = handlerMethod.getParameterTypes(); Object[] args = new Object[paramTypes.length]; @@ -155,6 +157,7 @@ public class HandlerMethodInvoker { GenericTypeResolver.resolveParameterType(methodParam, handler.getClass()); String paramName = null; boolean paramRequired = false; + String paramDefaultValue = null; String attrName = null; Object[] paramAnns = methodParam.getParameterAnnotations(); @@ -164,6 +167,7 @@ public class HandlerMethodInvoker { RequestParam requestParam = (RequestParam) paramAnn; paramName = requestParam.value(); paramRequired = requestParam.required(); + paramDefaultValue = requestParam.defaultValue(); break; } else if (ModelAttribute.class.isInstance(paramAnn)) { @@ -204,7 +208,8 @@ public class HandlerMethodInvoker { } if (paramName != null) { - args[i] = resolveRequestParam(paramName, paramRequired, methodParam, webRequest, handler); + args[i] = resolveRequestParam(paramName, paramRequired, paramDefaultValue, methodParam, webRequest, + handler); } else if (attrName != null) { WebDataBinder binder = resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler); @@ -238,7 +243,8 @@ public class HandlerMethodInvoker { Method methodToInvoke = BridgeMethodResolver.findBridgedMethod(initBinderMethod); String[] targetNames = AnnotationUtils.findAnnotation(methodToInvoke, InitBinder.class).value(); if (targetNames.length == 0 || Arrays.asList(targetNames).contains(attrName)) { - Object[] initBinderArgs = resolveInitBinderArguments(handler, methodToInvoke, binder, webRequest); + Object[] initBinderArgs = + resolveInitBinderArguments(handler, methodToInvoke, binder, webRequest); if (debug) { logger.debug("Invoking init-binder method: " + methodToInvoke); } @@ -253,8 +259,10 @@ public class HandlerMethodInvoker { } } - private Object[] resolveInitBinderArguments(Object handler, Method initBinderMethod, - WebDataBinder binder, NativeWebRequest webRequest) throws Exception { + private Object[] resolveInitBinderArguments(Object handler, + Method initBinderMethod, + WebDataBinder binder, + NativeWebRequest webRequest) throws Exception { Class[] initBinderParams = initBinderMethod.getParameterTypes(); Object[] initBinderArgs = new Object[initBinderParams.length]; @@ -265,6 +273,7 @@ public class HandlerMethodInvoker { GenericTypeResolver.resolveParameterType(methodParam, handler.getClass()); String paramName = null; boolean paramRequired = false; + String paramDefaultValue = null; Object[] paramAnns = methodParam.getParameterAnnotations(); for (int j = 0; j < paramAnns.length; j++) { @@ -273,6 +282,7 @@ public class HandlerMethodInvoker { RequestParam requestParam = (RequestParam) paramAnn; paramName = requestParam.value(); paramRequired = requestParam.required(); + paramDefaultValue = requestParam.defaultValue(); break; } else if (ModelAttribute.class.isInstance(paramAnn)) { @@ -302,16 +312,20 @@ public class HandlerMethodInvoker { } if (paramName != null) { - initBinderArgs[i] = resolveRequestParam(paramName, paramRequired, methodParam, webRequest, null); + initBinderArgs[i] = + resolveRequestParam(paramName, paramRequired, paramDefaultValue, methodParam, webRequest, null); } } return initBinderArgs; } - private Object resolveRequestParam(String paramName, boolean paramRequired, - MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall) - throws Exception { + private Object resolveRequestParam(String paramName, + boolean paramRequired, + String paramDefaultValue, + MethodParameter methodParam, + NativeWebRequest webRequest, + Object handlerForInitBinderCall) throws Exception { Class paramType = methodParam.getParameterType(); if ("".equals(paramName)) { @@ -332,10 +346,13 @@ public class HandlerMethodInvoker { } } if (paramValue == null) { - if (paramRequired) { + if (StringUtils.hasText(paramDefaultValue)) { + paramValue = paramDefaultValue; + } + else if (paramRequired) { raiseMissingParameterException(paramName, paramType); } - if (paramType.isPrimitive()) { + if (paramValue == null && paramType.isPrimitive()) { throw new IllegalStateException("Optional " + paramType + " parameter '" + paramName + "' is not present but cannot be translated into a null value due to being declared as a " + "primitive type. Consider declaring it as object wrapper for the corresponding primitive type."); @@ -346,8 +363,11 @@ public class HandlerMethodInvoker { return binder.convertIfNecessary(paramValue, paramType, methodParam); } - private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam, - ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception { + private WebDataBinder resolveModelAttribute(String attrName, + MethodParameter methodParam, + ExtendedModelMap implicitModel, + NativeWebRequest webRequest, + Object handler) throws Exception { // Bind request parameter onto object... String name = attrName; @@ -374,9 +394,10 @@ public class HandlerMethodInvoker { } @SuppressWarnings("unchecked") - public final void updateModelAttributes( - Object handler, Map mavModel, ExtendedModelMap implicitModel, NativeWebRequest webRequest) - throws Exception { + public final void updateModelAttributes(Object handler, + Map mavModel, + ExtendedModelMap implicitModel, + NativeWebRequest webRequest) throws Exception { if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) { for (String attrName : this.methodResolver.getActualSessionAttributeNames()) { @@ -407,8 +428,8 @@ public class HandlerMethodInvoker { } /** - * Determine whether the given value qualifies as a "binding candidate", - * i.e. might potentially be subject to bean-style data binding later on. + * Determine whether the given value qualifies as a "binding candidate", i.e. might potentially be subject to + * bean-style data binding later on. */ protected boolean isBindingCandidate(Object value) { return (value != null && !value.getClass().isArray() && !(value instanceof Collection) && @@ -426,7 +447,6 @@ public class HandlerMethodInvoker { throw new IllegalStateException("Should never get here"); } - protected void raiseMissingParameterException(String paramName, Class paramType) throws Exception { throw new IllegalStateException("Missing parameter '" + paramName + "' of type [" + paramType.getName() + "]"); } @@ -441,8 +461,7 @@ public class HandlerMethodInvoker { return new WebRequestDataBinder(target, objectName); } - protected void doBind(NativeWebRequest webRequest, WebDataBinder binder, boolean failOnErrors) - throws Exception { + protected void doBind(NativeWebRequest webRequest, WebDataBinder binder, boolean failOnErrors) throws Exception { WebRequestDataBinder requestBinder = (WebRequestDataBinder) binder; requestBinder.bind(webRequest); @@ -475,8 +494,7 @@ public class HandlerMethodInvoker { return value; } - protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest) - throws Exception { + protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest) throws Exception { if (WebRequest.class.isAssignableFrom(parameterType)) { return webRequest; diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java index 78fb102d3ac..92492cd2c99 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java @@ -25,14 +25,14 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; - import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import junit.framework.TestCase; +import static org.junit.Assert.*; +import org.junit.Test; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.aop.interceptor.SimpleTraceInterceptor; @@ -55,6 +55,7 @@ import org.springframework.ui.Model; import org.springframework.ui.ModelMap; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; +import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; @@ -82,11 +83,11 @@ import org.springframework.web.util.NestedServletException; * @author Sam Brannen * @since 2.5 */ -public class ServletAnnotationControllerTests extends TestCase { +public class ServletAnnotationControllerTests { - public void testStandardHandleMethod() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void standardHandleMethod() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MyController.class)); @@ -102,7 +103,61 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("test", response.getContentAsString()); } - public void testProxiedStandardHandleMethod() throws Exception { + @Test(expected = MissingServletRequestParameterException.class) + public void requiredParamMissing() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { + protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(RequiredParamController.class)); + wac.refresh(); + return wac; + } + }; + servlet.init(new MockServletConfig()); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + } + + @Test + public void optionalParamMissing() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { + protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(OptionalParamController.class)); + wac.refresh(); + return wac; + } + }; + servlet.init(new MockServletConfig()); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("null", response.getContentAsString()); + } + + @Test + public void defaultParamMissing() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { + protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(DefaultValueParamController.class)); + wac.refresh(); + return wac; + } + }; + servlet.init(new MockServletConfig()); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("foo", response.getContentAsString()); + } + + @Test + public void proxiedStandardHandleMethod() throws Exception { DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); @@ -110,7 +165,8 @@ public class ServletAnnotationControllerTests extends TestCase { DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); autoProxyCreator.setBeanFactory(wac.getBeanFactory()); wac.getBeanFactory().addBeanPostProcessor(autoProxyCreator); - wac.getBeanFactory().registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); + wac.getBeanFactory() + .registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); wac.refresh(); return wac; } @@ -123,12 +179,13 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("test", response.getContentAsString()); } - public void testEmptyParameterListHandleMethod() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void emptyParameterListHandleMethod() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(EmptyParameterListHandlerMethodController.class)); + wac.registerBeanDefinition("controller", + new RootBeanDefinition(EmptyParameterListHandlerMethodController.class)); RootBeanDefinition vrDef = new RootBeanDefinition(InternalResourceViewResolver.class); vrDef.getPropertyValues().addPropertyValue("suffix", ".jsp"); wac.registerBeanDefinition("viewResolver", vrDef); @@ -147,21 +204,23 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("", response.getContentAsString()); } - public void testAdaptedHandleMethods() throws Exception { + @Test + public void adaptedHandleMethods() throws Exception { doTestAdaptedHandleMethods(MyAdaptedController.class); } - public void testAdaptedHandleMethods2() throws Exception { + @Test + public void adaptedHandleMethods2() throws Exception { doTestAdaptedHandleMethods(MyAdaptedController2.class); } - public void testAdaptedHandleMethods3() throws Exception { + @Test + public void adaptedHandleMethods3() throws Exception { doTestAdaptedHandleMethods(MyAdaptedController3.class); } private void doTestAdaptedHandleMethods(final Class controllerClass) throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerClass)); @@ -204,9 +263,9 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("test-name1-typeMismatch", response.getContentAsString()); } - public void testFormController() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void formController() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MyFormController.class)); @@ -225,9 +284,9 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("myView-name1-typeMismatch-tb1-myValue", response.getContentAsString()); } - public void testModelFormController() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void modelFormController() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MyModelFormController.class)); @@ -246,7 +305,8 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("myView-name1-typeMismatch-tb1-myValue", response.getContentAsString()); } - public void testProxiedFormController() throws Exception { + @Test + public void proxiedFormController() throws Exception { DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); @@ -255,7 +315,8 @@ public class ServletAnnotationControllerTests extends TestCase { DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); autoProxyCreator.setBeanFactory(wac.getBeanFactory()); wac.getBeanFactory().addBeanPostProcessor(autoProxyCreator); - wac.getBeanFactory().registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); + wac.getBeanFactory() + .registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); wac.refresh(); return wac; } @@ -270,12 +331,13 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("myView-name1-typeMismatch-tb1-myValue", response.getContentAsString()); } - public void testCommandProvidingFormController() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void commandProvidingFormController() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(MyCommandProvidingFormController.class)); + wac.registerBeanDefinition("controller", + new RootBeanDefinition(MyCommandProvidingFormController.class)); wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class)); RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class); adapterDef.getPropertyValues().addPropertyValue("webBindingInitializer", new MyWebBindingInitializer()); @@ -295,16 +357,18 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); } - public void testTypedCommandProvidingFormController() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void typedCommandProvidingFormController() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(MyTypedCommandProvidingFormController.class)); + wac.registerBeanDefinition("controller", + new RootBeanDefinition(MyTypedCommandProvidingFormController.class)); wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class)); RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class); adapterDef.getPropertyValues().addPropertyValue("webBindingInitializer", new MyWebBindingInitializer()); - adapterDef.getPropertyValues().addPropertyValue("customArgumentResolver", new MySpecialArgumentResolver()); + adapterDef.getPropertyValues() + .addPropertyValue("customArgumentResolver", new MySpecialArgumentResolver()); wac.registerBeanDefinition("handlerAdapter", adapterDef); wac.refresh(); return wac; @@ -337,12 +401,13 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("myView-special-99-special-99", response.getContentAsString()); } - public void testBinderInitializingCommandProvidingFormController() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void binderInitializingCommandProvidingFormController() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(MyBinderInitializingCommandProvidingFormController.class)); + wac.registerBeanDefinition("controller", + new RootBeanDefinition(MyBinderInitializingCommandProvidingFormController.class)); wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class)); wac.refresh(); return wac; @@ -359,12 +424,13 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); } - public void testSpecificBinderInitializingCommandProvidingFormController() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void specificBinderInitializingCommandProvidingFormController() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(MySpecificBinderInitializingCommandProvidingFormController.class)); + wac.registerBeanDefinition("controller", + new RootBeanDefinition(MySpecificBinderInitializingCommandProvidingFormController.class)); wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class)); wac.refresh(); return wac; @@ -381,12 +447,12 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); } - public void testParameterDispatchingController() throws Exception { + @Test + public void parameterDispatchingController() throws Exception { final MockServletContext servletContext = new MockServletContext(); final MockServletConfig servletConfig = new MockServletConfig(servletContext); - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.setServletContext(servletContext); @@ -441,9 +507,9 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("mySurpriseView", response.getContentAsString()); } - public void testMethodNameDispatchingController() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void methodNameDispatchingController() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MethodNameDispatchingController.class)); @@ -474,9 +540,9 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("mySurpriseView", response.getContentAsString()); } - public void testMethodNameDispatchingControllerWithSuffix() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void methodNameDispatchingControllerWithSuffix() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MethodNameDispatchingController.class)); @@ -512,9 +578,9 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("mySurpriseView", response.getContentAsString()); } - public void testControllerClassNamePlusMethodNameDispatchingController() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void controllerClassNamePlusMethodNameDispatchingController() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); RootBeanDefinition mapping = new RootBeanDefinition(ControllerClassNameHandlerMapping.class); @@ -548,12 +614,13 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("mySurpriseView", response.getContentAsString()); } - public void testPostMethodNameDispatchingController() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void postMethodNameDispatchingController() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(MyPostMethodNameDispatchingController.class)); + wac.registerBeanDefinition("controller", + new RootBeanDefinition(MyPostMethodNameDispatchingController.class)); wac.refresh(); return wac; } @@ -596,12 +663,13 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("mySurpriseView", response.getContentAsString()); } - public void testRelativePathDispatchingController() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void relativePathDispatchingController() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); - wac.registerBeanDefinition("controller", new RootBeanDefinition(MyRelativePathDispatchingController.class)); + wac.registerBeanDefinition("controller", + new RootBeanDefinition(MyRelativePathDispatchingController.class)); wac.refresh(); return wac; } @@ -629,9 +697,9 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("mySurpriseView", response.getContentAsString()); } - public void testNullCommandController() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void nullCommandController() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MyNullCommandController.class)); @@ -648,9 +716,9 @@ public class ServletAnnotationControllerTests extends TestCase { assertEquals("myView", response.getContentAsString()); } - public void testEquivalentMappingsWithSameMethodName() throws Exception { - @SuppressWarnings("serial") - DispatcherServlet servlet = new DispatcherServlet() { + @Test + public void equivalentMappingsWithSameMethodName() throws Exception { + @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(ChildController.class)); @@ -672,128 +740,126 @@ public class ServletAnnotationControllerTests extends TestCase { } } - @RequestMapping("/myPath.do") private static class MyController extends AbstractController { - protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { + protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) + throws Exception { response.getWriter().write("test"); return null; } } - private static class BaseController { @RequestMapping(method = RequestMethod.GET) - public void myPath2(HttpServletResponse response) throws IOException { + public void myPath2(HttpServletResponse response) throws IOException { response.getWriter().write("test"); } } - @Controller private static class MyAdaptedController { @RequestMapping("/myPath1.do") - public void myHandle(HttpServletRequest request, HttpServletResponse response) throws IOException { + public void myHandle(HttpServletRequest request, HttpServletResponse response) throws IOException { response.getWriter().write("test"); } @RequestMapping("/myPath2.do") - public void myHandle(@RequestParam("param1") String p1, @RequestParam("param2") int p2, HttpServletResponse response) throws IOException { + public void myHandle(@RequestParam("param1")String p1, + @RequestParam("param2")int p2, + HttpServletResponse response) throws IOException { response.getWriter().write("test-" + p1 + "-" + p2); } @RequestMapping("/myPath3") - public void myHandle(TestBean tb, HttpServletResponse response) throws IOException { + public void myHandle(TestBean tb, HttpServletResponse response) throws IOException { response.getWriter().write("test-" + tb.getName() + "-" + tb.getAge()); } @RequestMapping("/myPath4.do") - public void myHandle(TestBean tb, Errors errors, HttpServletResponse response) throws IOException { + public void myHandle(TestBean tb, Errors errors, HttpServletResponse response) throws IOException { response.getWriter().write("test-" + tb.getName() + "-" + errors.getFieldError("age").getCode()); } } - @Controller @RequestMapping("/*.do") private static class MyAdaptedController2 { @RequestMapping - public void myHandle(HttpServletRequest request, HttpServletResponse response) throws IOException { + public void myHandle(HttpServletRequest request, HttpServletResponse response) throws IOException { response.getWriter().write("test"); } @RequestMapping("/myPath2.do") - public void myHandle(@RequestParam("param1") String p1, int param2, HttpServletResponse response) throws IOException { + public void myHandle(@RequestParam("param1")String p1, int param2, HttpServletResponse response) + throws IOException { response.getWriter().write("test-" + p1 + "-" + param2); } @RequestMapping("/myPath3") - public void myHandle(TestBean tb, HttpServletResponse response) throws IOException { + public void myHandle(TestBean tb, HttpServletResponse response) throws IOException { response.getWriter().write("test-" + tb.getName() + "-" + tb.getAge()); } @RequestMapping("/myPath4.*") - public void myHandle(TestBean tb, Errors errors, HttpServletResponse response) throws IOException { + public void myHandle(TestBean tb, Errors errors, HttpServletResponse response) throws IOException { response.getWriter().write("test-" + tb.getName() + "-" + errors.getFieldError("age").getCode()); } } - @Controller private static class MyAdaptedControllerBase { @RequestMapping("/myPath2.do") - public void myHandle(@RequestParam("param1") T p1, int param2, HttpServletResponse response) throws IOException { + public void myHandle(@RequestParam("param1")T p1, int param2, HttpServletResponse response) throws IOException { response.getWriter().write("test-" + p1 + "-" + param2); } @InitBinder - public void initBinder(@RequestParam("param1") T p1, int param2) { + public void initBinder(@RequestParam("param1")T p1, int param2) { } @ModelAttribute - public void modelAttribute(@RequestParam("param1") T p1, int param2) { + public void modelAttribute(@RequestParam("param1")T p1, int param2) { } } - @RequestMapping("/*.do") private static class MyAdaptedController3 extends MyAdaptedControllerBase { @RequestMapping - public void myHandle(HttpServletRequest request, HttpServletResponse response) throws IOException { + public void myHandle(HttpServletRequest request, HttpServletResponse response) throws IOException { response.getWriter().write("test"); } @Override - public void myHandle(@RequestParam("param1") String p1, int param2, HttpServletResponse response) throws IOException { + public void myHandle(@RequestParam("param1")String p1, int param2, HttpServletResponse response) + throws IOException { response.getWriter().write("test-" + p1 + "-" + param2); } @RequestMapping("/myPath3") - public void myHandle(TestBean tb, HttpServletResponse response) throws IOException { + public void myHandle(TestBean tb, HttpServletResponse response) throws IOException { response.getWriter().write("test-" + tb.getName() + "-" + tb.getAge()); } @RequestMapping("/myPath4.*") - public void myHandle(TestBean tb, Errors errors, HttpServletResponse response) throws IOException { + public void myHandle(TestBean tb, Errors errors, HttpServletResponse response) throws IOException { response.getWriter().write("test-" + tb.getName() + "-" + errors.getFieldError("age").getCode()); } @InitBinder - public void initBinder(@RequestParam("param1") String p1, int param2) { + public void initBinder(@RequestParam("param1")String p1, int param2) { } @ModelAttribute - public void modelAttribute(@RequestParam("param1") String p1, int param2) { + public void modelAttribute(@RequestParam("param1")String p1, int param2) { } } - @Controller @RequestMapping(method = RequestMethod.GET) private static class EmptyParameterListHandlerMethodController { @@ -810,7 +876,6 @@ public class ServletAnnotationControllerTests extends TestCase { } } - @Controller public static class MyFormController { @@ -823,7 +888,7 @@ public class ServletAnnotationControllerTests extends TestCase { } @RequestMapping("/myPath.do") - public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, ModelMap model) { + public String myHandle(@ModelAttribute("myCommand")TestBean tb, BindingResult errors, ModelMap model) { if (!model.containsKey("myKey")) { model.addAttribute("myKey", "myValue"); } @@ -831,7 +896,6 @@ public class ServletAnnotationControllerTests extends TestCase { } } - @Controller public static class MyModelFormController { @@ -844,7 +908,7 @@ public class ServletAnnotationControllerTests extends TestCase { } @RequestMapping("/myPath.do") - public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, Model model) { + public String myHandle(@ModelAttribute("myCommand")TestBean tb, BindingResult errors, Model model) { if (!model.containsAttribute("myKey")) { model.addAttribute("myKey", "myValue"); } @@ -852,20 +916,20 @@ public class ServletAnnotationControllerTests extends TestCase { } } - @Controller private static class MyCommandProvidingFormController extends MyFormController { @SuppressWarnings("unused") @ModelAttribute("myCommand") - private TestBean createTestBean( - @RequestParam T defaultName, Map model, @RequestParam Date date) { + private TestBean createTestBean(@RequestParam T defaultName, + Map model, + @RequestParam Date date) { model.put("myKey", "myOriginalValue"); return new TestBean(defaultName.getClass().getSimpleName() + ":" + defaultName.toString()); } @RequestMapping("/myPath.do") - public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, ModelMap model) { + public String myHandle(@ModelAttribute("myCommand")TestBean tb, BindingResult errors, ModelMap model) { return super.myHandle(tb, errors, model); } @@ -890,19 +954,17 @@ public class ServletAnnotationControllerTests extends TestCase { } } - private static class MySpecialArg { public MySpecialArg(String value) { } } - @Controller private static class MyTypedCommandProvidingFormController extends MyCommandProvidingFormController { - } + } @Controller private static class MyBinderInitializingCommandProvidingFormController extends MyCommandProvidingFormController { @@ -917,13 +979,13 @@ public class ServletAnnotationControllerTests extends TestCase { } } - @Controller - private static class MySpecificBinderInitializingCommandProvidingFormController extends MyCommandProvidingFormController { + private static class MySpecificBinderInitializingCommandProvidingFormController + extends MyCommandProvidingFormController { @SuppressWarnings("unused") @InitBinder({"myCommand", "date"}) - private void initBinder(WebDataBinder binder, String date, @RequestParam("date") String[] date2) { + private void initBinder(WebDataBinder binder, String date, @RequestParam("date")String[] date2) { assertEquals("2007-10-02", date); assertEquals(1, date2.length); assertEquals("2007-10-02", date2[0]); @@ -933,7 +995,6 @@ public class ServletAnnotationControllerTests extends TestCase { } } - private static class MyWebBindingInitializer implements WebBindingInitializer { public void initBinder(WebDataBinder binder, WebRequest request) { @@ -944,7 +1005,6 @@ public class ServletAnnotationControllerTests extends TestCase { } } - private static class MySpecialArgumentResolver implements WebArgumentResolver { public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) { @@ -955,7 +1015,6 @@ public class ServletAnnotationControllerTests extends TestCase { } } - @Controller @RequestMapping("/myPath.do") private static class MyParameterDispatchingController { @@ -974,7 +1033,8 @@ public class ServletAnnotationControllerTests extends TestCase { @RequestMapping public void myHandle(HttpServletResponse response, HttpServletRequest request) throws IOException { - if (this.servletContext == null || this.servletConfig == null || this.session == null || this.request == null) { + if (this.servletContext == null || this.servletConfig == null || this.session == null || + this.request == null) { throw new IllegalStateException(); } response.getWriter().write("myView"); @@ -1000,14 +1060,12 @@ public class ServletAnnotationControllerTests extends TestCase { } } - @Controller @RequestMapping(value = "/*.do", method = RequestMethod.POST, params = "myParam=myValue") private static class MyPostMethodNameDispatchingController extends MethodNameDispatchingController { } - @Controller @RequestMapping("/myApp/*") private static class MyRelativePathDispatchingController { @@ -1033,7 +1091,6 @@ public class ServletAnnotationControllerTests extends TestCase { } } - @Controller private static class MyNullCommandController { @@ -1048,8 +1105,11 @@ public class ServletAnnotationControllerTests extends TestCase { } @RequestMapping("/myPath") - public void handle(@ModelAttribute TestBean testBean, Errors errors, - @ModelAttribute TestPrincipal modelPrinc, OtherPrincipal requestPrinc, Writer writer) throws IOException { + public void handle(@ModelAttribute TestBean testBean, + Errors errors, + @ModelAttribute TestPrincipal modelPrinc, + OtherPrincipal requestPrinc, + Writer writer) throws IOException { assertNull(testBean); assertNotNull(modelPrinc); assertNotNull(requestPrinc); @@ -1059,7 +1119,6 @@ public class ServletAnnotationControllerTests extends TestCase { } } - private static class TestPrincipal implements Principal { public String getName() { @@ -1067,7 +1126,6 @@ public class ServletAnnotationControllerTests extends TestCase { } } - private static class OtherPrincipal implements Principal { public String getName() { @@ -1075,7 +1133,6 @@ public class ServletAnnotationControllerTests extends TestCase { } } - private static class TestViewResolver implements ViewResolver { public View resolveViewName(final String viewName, Locale locale) throws Exception { @@ -1083,8 +1140,10 @@ public class ServletAnnotationControllerTests extends TestCase { public String getContentType() { return null; } + @SuppressWarnings({"unchecked", "deprecation"}) - public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { + public void render(Map model, HttpServletRequest request, HttpServletResponse response) + throws Exception { TestBean tb = (TestBean) model.get("testBean"); if (tb == null) { tb = (TestBean) model.get("myCommand"); @@ -1104,8 +1163,9 @@ public class ServletAnnotationControllerTests extends TestCase { } List testBeans = (List) model.get("testBeanList"); if (errors.hasFieldErrors("age")) { - response.getWriter().write(viewName + "-" + tb.getName() + "-" + errors.getFieldError("age").getCode() + - "-" + testBeans.get(0).getName() + "-" + model.get("myKey")); + response.getWriter().write(viewName + "-" + tb.getName() + "-" + + errors.getFieldError("age").getCode() + "-" + testBeans.get(0).getName() + "-" + + model.get("myKey")); } else { response.getWriter().write(viewName + "-" + tb.getName() + "-" + tb.getAge() + "-" + @@ -1116,7 +1176,6 @@ public class ServletAnnotationControllerTests extends TestCase { } } - public static class ParentController { @RequestMapping(method = RequestMethod.GET) @@ -1124,13 +1183,41 @@ public class ServletAnnotationControllerTests extends TestCase { } } - @Controller @RequestMapping("/child/test") public static class ChildController extends ParentController { @RequestMapping(method = RequestMethod.GET) - public void doGet(HttpServletRequest req, HttpServletResponse resp, @RequestParam("childId") String id) { + public void doGet(HttpServletRequest req, HttpServletResponse resp, @RequestParam("childId")String id) { + } + } + + @Controller + public static class RequiredParamController { + + @RequestMapping("/myPath.do") + public void myHandle(@RequestParam(value = "id", required = true)String id) { + + } + } + + @Controller + public static class OptionalParamController { + + @RequestMapping("/myPath.do") + public void myHandle(@RequestParam(value = "id", required = false)String id, HttpServletResponse response) + throws IOException { + response.getWriter().write(String.valueOf(id)); + } + } + + @Controller + public static class DefaultValueParamController { + + @RequestMapping("/myPath.do") + public void myHandle(@RequestParam(value = "id", defaultValue = "foo")String id, HttpServletResponse response) + throws IOException { + response.getWriter().write(String.valueOf(id)); } } diff --git a/org.springframework.web.servlet/web-servlet.iml b/org.springframework.web.servlet/web-servlet.iml index a5efdb76e9a..0cb0ce5c672 100644 --- a/org.springframework.web.servlet/web-servlet.iml +++ b/org.springframework.web.servlet/web-servlet.iml @@ -11,6 +11,7 @@ + @@ -170,17 +171,6 @@ - - - - - - - - - - - @@ -203,6 +193,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +