/* * Copyright 2002-2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.web.servlet.mvc.annotation; import java.io.IOException; import java.io.Writer; import java.security.Principal; import java.text.SimpleDateFormat; import java.util.Date; 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 org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.aop.interceptor.SimpleTraceInterceptor; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.beans.DerivedTestBean; import org.springframework.beans.ITestBean; import org.springframework.beans.TestBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.core.MethodParameter; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletConfig; import org.springframework.mock.web.MockServletContext; import org.springframework.stereotype.Controller; import org.springframework.ui.ExtendedModelMap; 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.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.support.WebArgumentResolver; import org.springframework.web.bind.support.WebBindingInitializer; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.mvc.AbstractController; import org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver; import org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.util.NestedServletException; /** * @author Juergen Hoeller * @author Sam Brannen * @since 2.5 */ public class ServletAnnotationControllerTests extends TestCase { public void testStandardHandleMethod() throws Exception { @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MyController.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("test", response.getContentAsString()); } public void testProxiedStandardHandleMethod() throws Exception { DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MyController.class)); DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); autoProxyCreator.setBeanFactory(wac.getBeanFactory()); wac.getBeanFactory().addBeanPostProcessor(autoProxyCreator); wac.getBeanFactory().registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("test", response.getContentAsString()); } public void testEmptyParameterListHandleMethod() throws Exception { @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(EmptyParameterListHandlerMethodController.class)); RootBeanDefinition vrDef = new RootBeanDefinition(InternalResourceViewResolver.class); vrDef.getPropertyValues().addPropertyValue("suffix", ".jsp"); wac.registerBeanDefinition("viewResolver", vrDef); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/emptyParameterListHandler"); MockHttpServletResponse response = new MockHttpServletResponse(); EmptyParameterListHandlerMethodController.called = false; servlet.service(request, response); assertTrue(EmptyParameterListHandlerMethodController.called); assertEquals("", response.getContentAsString()); } public void testAdaptedHandleMethods() throws Exception { doTestAdaptedHandleMethods(MyAdaptedController.class); } public void testAdaptedHandleMethods2() throws Exception { doTestAdaptedHandleMethods(MyAdaptedController2.class); } public void testAdaptedHandleMethods3() throws Exception { doTestAdaptedHandleMethods(MyAdaptedController3.class); } private void doTestAdaptedHandleMethods(final Class<?> controllerClass) throws Exception { @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerClass)); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath1.do"); MockHttpServletResponse response = new MockHttpServletResponse(); request.addParameter("param1", "value1"); request.addParameter("param2", "2"); servlet.service(request, response); assertEquals("test", response.getContentAsString()); request = new MockHttpServletRequest("GET", "/myPath2.do"); request.addParameter("param1", "value1"); request.addParameter("param2", "2"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("test-value1-2", response.getContentAsString()); request = new MockHttpServletRequest("GET", "/myPath3.do"); request.addParameter("param1", "value1"); request.addParameter("param2", "2"); request.addParameter("name", "name1"); request.addParameter("age", "2"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("test-name1-2", response.getContentAsString()); request = new MockHttpServletRequest("GET", "/myPath4.do"); request.addParameter("param1", "value1"); request.addParameter("param2", "2"); request.addParameter("name", "name1"); request.addParameter("age", "value2"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("test-name1-typeMismatch", response.getContentAsString()); } public void testFormController() throws Exception { @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MyFormController.class)); wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class)); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); request.addParameter("name", "name1"); request.addParameter("age", "value2"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView-name1-typeMismatch-tb1-myValue", response.getContentAsString()); } public void testModelFormController() throws Exception { @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MyModelFormController.class)); wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class)); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); request.addParameter("name", "name1"); request.addParameter("age", "value2"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView-name1-typeMismatch-tb1-myValue", response.getContentAsString()); } public void testProxiedFormController() throws Exception { DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MyFormController.class)); wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class)); DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); autoProxyCreator.setBeanFactory(wac.getBeanFactory()); wac.getBeanFactory().addBeanPostProcessor(autoProxyCreator); wac.getBeanFactory().registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); request.addParameter("name", "name1"); request.addParameter("age", "value2"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView-name1-typeMismatch-tb1-myValue", response.getContentAsString()); } public void testCommandProvidingFormController() 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("viewResolver", new RootBeanDefinition(TestViewResolver.class)); RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class); adapterDef.getPropertyValues().addPropertyValue("webBindingInitializer", new MyWebBindingInitializer()); wac.registerBeanDefinition("handlerAdapter", adapterDef); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); request.addParameter("defaultName", "myDefaultName"); request.addParameter("age", "value2"); request.addParameter("date", "2007-10-02"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); } public void testTypedCommandProvidingFormController() 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("viewResolver", new RootBeanDefinition(TestViewResolver.class)); RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class); adapterDef.getPropertyValues().addPropertyValue("webBindingInitializer", new MyWebBindingInitializer()); adapterDef.getPropertyValues().addPropertyValue("customArgumentResolver", new MySpecialArgumentResolver()); wac.registerBeanDefinition("handlerAdapter", adapterDef); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); request.addParameter("defaultName", "10"); request.addParameter("age", "value2"); request.addParameter("date", "2007-10-02"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView-Integer:10-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); request = new MockHttpServletRequest("GET", "/myOtherPath.do"); request.addParameter("defaultName", "10"); request.addParameter("age", "value2"); request.addParameter("date", "2007-10-02"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView-myName-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); request = new MockHttpServletRequest("GET", "/myThirdPath.do"); request.addParameter("defaultName", "10"); request.addParameter("age", "100"); request.addParameter("date", "2007-10-02"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView-special-99-special-99", response.getContentAsString()); } public void testBinderInitializingCommandProvidingFormController() 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("viewResolver", new RootBeanDefinition(TestViewResolver.class)); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); request.addParameter("defaultName", "myDefaultName"); request.addParameter("age", "value2"); request.addParameter("date", "2007-10-02"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); } public void testSpecificBinderInitializingCommandProvidingFormController() 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("viewResolver", new RootBeanDefinition(TestViewResolver.class)); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); request.addParameter("defaultName", "myDefaultName"); request.addParameter("age", "value2"); request.addParameter("date", "2007-10-02"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString()); } public void testParameterDispatchingController() throws Exception { final MockServletContext servletContext = new MockServletContext(); final MockServletConfig servletConfig = new MockServletConfig(servletContext); @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.setServletContext(servletContext); RootBeanDefinition bd = new RootBeanDefinition(MyParameterDispatchingController.class); bd.setScope(WebApplicationContext.SCOPE_REQUEST); wac.registerBeanDefinition("controller", bd); AnnotationConfigUtils.registerAnnotationConfigProcessors(wac); wac.getBeanFactory().registerResolvableDependency(ServletConfig.class, servletConfig); wac.refresh(); return wac; } }; servlet.init(servletConfig); MockHttpServletRequest request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do"); MockHttpServletResponse response = new MockHttpServletResponse(); HttpSession session = request.getSession(); servlet.service(request, response); assertEquals("myView", response.getContentAsString()); assertSame(servletContext, request.getAttribute("servletContext")); assertSame(servletConfig, request.getAttribute("servletConfig")); assertSame(session, request.getAttribute("session")); assertSame(request, request.getAttribute("request")); request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do"); response = new MockHttpServletResponse(); session = request.getSession(); servlet.service(request, response); assertEquals("myView", response.getContentAsString()); assertSame(servletContext, request.getAttribute("servletContext")); assertSame(servletConfig, request.getAttribute("servletConfig")); assertSame(session, request.getAttribute("session")); assertSame(request, request.getAttribute("request")); request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do"); request.addParameter("view", "other"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myOtherView", response.getContentAsString()); request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do"); request.addParameter("view", "my"); request.addParameter("lang", "de"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myLangView", response.getContentAsString()); request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do"); request.addParameter("surprise", "!"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("mySurpriseView", response.getContentAsString()); } public void testMethodNameDispatchingController() throws Exception { @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MethodNameDispatchingController.class)); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myHandle.do"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView", response.getContentAsString()); request = new MockHttpServletRequest("GET", "/myOtherHandle.do"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myOtherView", response.getContentAsString()); request = new MockHttpServletRequest("POST", "/myLangHandle.do"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myLangView", response.getContentAsString()); request = new MockHttpServletRequest("POST", "/mySurpriseHandle.do"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("mySurpriseView", response.getContentAsString()); } public void testMethodNameDispatchingControllerWithSuffix() throws Exception { @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MethodNameDispatchingController.class)); InternalPathMethodNameResolver methodNameResolver = new InternalPathMethodNameResolver(); methodNameResolver.setSuffix("Handle"); RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class); adapterDef.getPropertyValues().addPropertyValue("methodNameResolver", methodNameResolver); wac.registerBeanDefinition("handlerAdapter", adapterDef); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/my.do"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView", response.getContentAsString()); request = new MockHttpServletRequest("GET", "/myOther.do"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myOtherView", response.getContentAsString()); request = new MockHttpServletRequest("POST", "/myLang.do"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myLangView", response.getContentAsString()); request = new MockHttpServletRequest("POST", "/mySurprise.do"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("mySurpriseView", response.getContentAsString()); } public void testControllerClassNamePlusMethodNameDispatchingController() throws Exception { @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); RootBeanDefinition mapping = new RootBeanDefinition(ControllerClassNameHandlerMapping.class); mapping.getPropertyValues().addPropertyValue("excludedPackages", null); wac.registerBeanDefinition("handlerMapping", mapping); wac.registerBeanDefinition("controller", new RootBeanDefinition(MethodNameDispatchingController.class)); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/methodnamedispatching/myHandle"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView", response.getContentAsString()); request = new MockHttpServletRequest("GET", "/methodnamedispatching/myOtherHandle.do"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myOtherView", response.getContentAsString()); request = new MockHttpServletRequest("POST", "/methodnamedispatching/myLangHandle.x"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myLangView", response.getContentAsString()); request = new MockHttpServletRequest("POST", "/methodnamedispatching/mySurpriseHandle.y"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("mySurpriseView", response.getContentAsString()); } public void testPostMethodNameDispatchingController() 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.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myHandle.do"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals(405, response.getStatus()); request = new MockHttpServletRequest("POST", "/myUnknownHandle.do"); request.addParameter("myParam", "myValue"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals(404, response.getStatus()); request = new MockHttpServletRequest("POST", "/myHandle.do"); request.addParameter("myParam", "myValue"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView", response.getContentAsString()); request = new MockHttpServletRequest("POST", "/myOtherHandle.do"); request.addParameter("myParam", "myValue"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myOtherView", response.getContentAsString()); request = new MockHttpServletRequest("POST", "/myLangHandle.do"); request.addParameter("myParam", "myValue"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myLangView", response.getContentAsString()); request = new MockHttpServletRequest("POST", "/mySurpriseHandle.do"); request.addParameter("myParam", "myValue"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("mySurpriseView", response.getContentAsString()); } public void testRelativePathDispatchingController() 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.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myApp/myHandle"); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView", response.getContentAsString()); request = new MockHttpServletRequest("GET", "/myApp/myOther"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myOtherView", response.getContentAsString()); request = new MockHttpServletRequest("GET", "/myApp/myLang"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myLangView", response.getContentAsString()); request = new MockHttpServletRequest("GET", "/myApp/surprise.do"); response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("mySurpriseView", response.getContentAsString()); } public void testNullCommandController() throws Exception { @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(MyNullCommandController.class)); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath"); request.setUserPrincipal(new OtherPrincipal()); MockHttpServletResponse response = new MockHttpServletResponse(); servlet.service(request, response); assertEquals("myView", response.getContentAsString()); } public void testEquivalentMappingsWithSameMethodName() throws Exception { @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { GenericWebApplicationContext wac = new GenericWebApplicationContext(); wac.registerBeanDefinition("controller", new RootBeanDefinition(ChildController.class)); wac.refresh(); return wac; } }; servlet.init(new MockServletConfig()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/child/test"); request.addParameter("childId", "100"); MockHttpServletResponse response = new MockHttpServletResponse(); try { servlet.service(request, response); } catch (NestedServletException ex) { assertTrue(ex.getCause() instanceof IllegalStateException); assertTrue(ex.getCause().getMessage().contains("doGet")); } } @RequestMapping("/myPath.do") private static class MyController extends AbstractController { 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 { response.getWriter().write("test"); } } @Controller private static class MyAdaptedController { @RequestMapping("/myPath1.do") 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 { response.getWriter().write("test-" + p1 + "-" + p2); } @RequestMapping("/myPath3") 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 { 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 { response.getWriter().write("test"); } @RequestMapping("/myPath2.do") 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 { response.getWriter().write("test-" + tb.getName() + "-" + tb.getAge()); } @RequestMapping("/myPath4.*") 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<T> { @RequestMapping("/myPath2.do") 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) { } @ModelAttribute public void modelAttribute(@RequestParam("param1") T p1, int param2) { } } @RequestMapping("/*.do") private static class MyAdaptedController3 extends MyAdaptedControllerBase<String> { @RequestMapping 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 { response.getWriter().write("test-" + p1 + "-" + param2); } @RequestMapping("/myPath3") 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 { response.getWriter().write("test-" + tb.getName() + "-" + errors.getFieldError("age").getCode()); } @InitBinder public void initBinder(@RequestParam("param1") String p1, int param2) { } @ModelAttribute public void modelAttribute(@RequestParam("param1") String p1, int param2) { } } @Controller @RequestMapping(method = RequestMethod.GET) private static class EmptyParameterListHandlerMethodController { static boolean called; @RequestMapping("/emptyParameterListHandler") public void emptyParameterListHandler() { EmptyParameterListHandlerMethodController.called = true; } @RequestMapping("/nonEmptyParameterListHandler") public void nonEmptyParameterListHandler(HttpServletResponse response) { } } @Controller public static class MyFormController { @ModelAttribute("testBeanList") public List<TestBean> getTestBeans() { List<TestBean> list = new LinkedList<TestBean>(); list.add(new TestBean("tb1")); list.add(new TestBean("tb2")); return list; } @RequestMapping("/myPath.do") public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, ModelMap model) { if (!model.containsKey("myKey")) { model.addAttribute("myKey", "myValue"); } return "myView"; } } @Controller public static class MyModelFormController { @ModelAttribute public List<TestBean> getTestBeans() { List<TestBean> list = new LinkedList<TestBean>(); list.add(new TestBean("tb1")); list.add(new TestBean("tb2")); return list; } @RequestMapping("/myPath.do") public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, Model model) { if (!model.containsAttribute("myKey")) { model.addAttribute("myKey", "myValue"); } return "myView"; } } @Controller private static class MyCommandProvidingFormController<T, TB, TB2> extends MyFormController { @SuppressWarnings("unused") @ModelAttribute("myCommand") private TestBean createTestBean( @RequestParam T defaultName, Map<String, Object> 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) { return super.myHandle(tb, errors, model); } @RequestMapping("/myOtherPath.do") public String myOtherHandle(TB tb, BindingResult errors, ExtendedModelMap model, MySpecialArg arg) { TestBean tbReal = (TestBean) tb; tbReal.setName("myName"); assertTrue(model.get("ITestBean") instanceof DerivedTestBean); assertNotNull(arg); return super.myHandle(tbReal, errors, model); } @RequestMapping("/myThirdPath.do") public String myThirdHandle(TB tb, Model model) { model.addAttribute("testBean", new TestBean("special", 99)); return "myView"; } @ModelAttribute protected TB2 getModelAttr() { return (TB2) new DerivedTestBean(); } } private static class MySpecialArg { public MySpecialArg(String value) { } } @Controller private static class MyTypedCommandProvidingFormController extends MyCommandProvidingFormController<Integer, TestBean, ITestBean> { } @Controller private static class MyBinderInitializingCommandProvidingFormController extends MyCommandProvidingFormController { @SuppressWarnings("unused") @InitBinder private void initBinder(WebDataBinder binder) { binder.initBeanPropertyAccess(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); } } @Controller private static class MySpecificBinderInitializingCommandProvidingFormController extends MyCommandProvidingFormController { @SuppressWarnings("unused") @InitBinder({"myCommand", "date"}) 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]); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); } } private static class MyWebBindingInitializer implements WebBindingInitializer { public void initBinder(WebDataBinder binder, WebRequest request) { assertNotNull(request.getLocale()); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); } } private static class MySpecialArgumentResolver implements WebArgumentResolver { public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) { if (methodParameter.getParameterType().equals(MySpecialArg.class)) { return new MySpecialArg("myValue"); } return UNRESOLVED; } } @Controller @RequestMapping("/myPath.do") private static class MyParameterDispatchingController { @Autowired private ServletContext servletContext; @Autowired private ServletConfig servletConfig; @Autowired private HttpSession session; @Autowired private HttpServletRequest request; @RequestMapping public void myHandle(HttpServletResponse response, HttpServletRequest request) throws IOException { if (this.servletContext == null || this.servletConfig == null || this.session == null || this.request == null) { throw new IllegalStateException(); } response.getWriter().write("myView"); request.setAttribute("servletContext", this.servletContext); request.setAttribute("servletConfig", this.servletConfig); request.setAttribute("session", this.session); request.setAttribute("request", this.request); } @RequestMapping(params = {"view", "!lang"}) public void myOtherHandle(HttpServletResponse response) throws IOException { response.getWriter().write("myOtherView"); } @RequestMapping(method = RequestMethod.GET, params = {"view=my", "lang=de"}) public void myLangHandle(HttpServletResponse response) throws IOException { response.getWriter().write("myLangView"); } @RequestMapping(method = {RequestMethod.POST, RequestMethod.GET}, params = "surprise") public void mySurpriseHandle(HttpServletResponse response) throws IOException { response.getWriter().write("mySurpriseView"); } } @Controller @RequestMapping(value = "/*.do", method = RequestMethod.POST, params = "myParam=myValue") private static class MyPostMethodNameDispatchingController extends MethodNameDispatchingController { } @Controller @RequestMapping("/myApp/*") private static class MyRelativePathDispatchingController { @RequestMapping public void myHandle(HttpServletResponse response) throws IOException { response.getWriter().write("myView"); } @RequestMapping("*Other") public void myOtherHandle(HttpServletResponse response) throws IOException { response.getWriter().write("myOtherView"); } @RequestMapping("myLang") public void myLangHandle(HttpServletResponse response) throws IOException { response.getWriter().write("myLangView"); } @RequestMapping("surprise") public void mySurpriseHandle(HttpServletResponse response) throws IOException { response.getWriter().write("mySurpriseView"); } } @Controller private static class MyNullCommandController { @ModelAttribute public TestBean getTestBean() { return null; } @ModelAttribute public Principal getPrincipal() { return new TestPrincipal(); } @RequestMapping("/myPath") public void handle(@ModelAttribute TestBean testBean, Errors errors, @ModelAttribute TestPrincipal modelPrinc, OtherPrincipal requestPrinc, Writer writer) throws IOException { assertNull(testBean); assertNotNull(modelPrinc); assertNotNull(requestPrinc); assertFalse(errors.hasErrors()); errors.reject("myCode"); writer.write("myView"); } } private static class TestPrincipal implements Principal { public String getName() { return "test"; } } private static class OtherPrincipal implements Principal { public String getName() { return "other"; } } private static class TestViewResolver implements ViewResolver { public View resolveViewName(final String viewName, Locale locale) throws Exception { return new View() { public String getContentType() { return null; } @SuppressWarnings({"unchecked", "deprecation"}) 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"); } if (tb.getName().endsWith("myDefaultName")) { assertTrue(tb.getDate().getYear() == 107); } Errors errors = (Errors) model.get(BindingResult.MODEL_KEY_PREFIX + "testBean"); if (errors == null) { errors = (Errors) model.get(BindingResult.MODEL_KEY_PREFIX + "myCommand"); } if (errors.hasFieldErrors("date")) { throw new IllegalStateException(); } if (model.containsKey("ITestBean")) { assertTrue(model.get(BindingResult.MODEL_KEY_PREFIX + "ITestBean") instanceof Errors); } List<TestBean> testBeans = (List<TestBean>) model.get("testBeanList"); if (errors.hasFieldErrors("age")) { 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() + "-" + errors.getFieldValue("name") + "-" + errors.getFieldValue("age")); } } }; } } public static class ParentController { @RequestMapping(method = RequestMethod.GET) public void doGet(HttpServletRequest req, HttpServletResponse resp) { } } @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) { } } }