From 3ee6734424df5dab2ab7de8092b16f4ff5243071 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 6 Apr 2011 15:30:01 +0000 Subject: [PATCH] SPR-8216 Replicate ServletAnnotationControllerTests for HandlerMethod infrastructure --- .../annotation/ServletHandlerMethodTests.java | 2691 +++++++++++++++++ 1 file changed, 2691 insertions(+) create mode 100644 org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletHandlerMethodTests.java diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletHandlerMethodTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletHandlerMethodTests.java new file mode 100644 index 0000000000..d6668915ef --- /dev/null +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletHandlerMethodTests.java @@ -0,0 +1,2691 @@ +/* + * Copyright 2002-2010 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.method.annotation; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.beans.PropertyEditorSupport; +import java.io.IOException; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Method; +import java.security.Principal; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.xml.bind.annotation.XmlRootElement; + +import org.junit.Ignore; +import org.junit.Test; +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.GenericBean; +import org.springframework.beans.ITestBean; +import org.springframework.beans.TestBean; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; +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.core.convert.converter.Converter; +import org.springframework.format.support.FormattingConversionServiceFactoryBean; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.ByteArrayHttpMessageConverter; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.mock.web.MockMultipartHttpServletRequest; +import org.springframework.mock.web.MockServletConfig; +import org.springframework.mock.web.MockServletContext; +import org.springframework.oxm.jaxb.Jaxb2Marshaller; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ExtendedModelMap; +import org.springframework.ui.Model; +import org.springframework.ui.ModelMap; +import org.springframework.util.MultiValueMap; +import org.springframework.util.SerializationTestUtils; +import org.springframework.util.StringUtils; +import org.springframework.validation.BindingResult; +import org.springframework.validation.Errors; +import org.springframework.validation.FieldError; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +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.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.SessionAttributes; +import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; +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.multipart.support.StringMultipartFileEditor; +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.annotation.ModelAndViewResolver; +import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver; +import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; +import org.springframework.web.servlet.view.InternalResourceViewResolver; +import org.springframework.web.util.NestedServletException; + +/** + * The origin of this test fixture is {@link ServletHandlerMethodTests}. The tests were adapted to run against + * the HandlerMethod infrastructure rather than against the DefaultAnnotationHandlerMapping, the + * AnnotationMethodHandlerAdapter, and the AnnotationMethodHandlerExceptionResolver. + * Tests that are not supported with HandlerMethod processing have been deleted and are listed at the bottom. + * + * @author Rossen Stoyanchev + * @since 3.1 + */ +public class ServletHandlerMethodTests { + + private DispatcherServlet servlet; + + @Test + public void emptyValueMapping() throws Exception { + initDispatcherServlet(ControllerWithEmptyValueMapping.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); + request.setContextPath("/foo"); + request.setServletPath(""); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("test", response.getContentAsString()); + } + + @Test + public void customAnnotationController() throws Exception { + initDispatcherServlet(CustomAnnotationController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Invalid response status code", HttpServletResponse.SC_OK, response.getStatus()); + } + + @Test + public void requiredParamMissing() throws Exception { + initDispatcherServlet(RequiredParamController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Invalid response status code", HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + assertTrue(servlet.getWebApplicationContext().isSingleton("controller")); + } + + @Test + public void typeConversionError() throws Exception { + initDispatcherServlet(RequiredParamController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); + request.addParameter("id", "foo"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Invalid response status code", HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + } + + @Test + public void optionalParamPresent() throws Exception { + initDispatcherServlet(OptionalParamController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); + request.addParameter("id", "val"); + request.addParameter("flag", "true"); + request.addHeader("header", "otherVal"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("val-true-otherVal", response.getContentAsString()); + } + + @Test + public void optionalParamMissing() throws Exception { + initDispatcherServlet(OptionalParamController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("null-false-null", response.getContentAsString()); + } + + @Test + public void defaultParameters() throws Exception { + initDispatcherServlet(DefaultValueParamController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("foo--bar", response.getContentAsString()); + } + + @Test + public void defaultExpressionParameters() throws Exception { + initDispatcherServlet(DefaultExpressionValueParamController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext context) { + RootBeanDefinition ppc = new RootBeanDefinition(PropertyPlaceholderConfigurer.class); + ppc.getPropertyValues().add("properties", "myKey=foo"); + context.registerBeanDefinition("ppc", ppc); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myApp/myPath.do"); + request.setContextPath("/myApp"); + MockHttpServletResponse response = new MockHttpServletResponse(); + System.setProperty("myHeader", "bar"); + try { + servlet.service(request, response); + } + finally { + System.clearProperty("myHeader"); + } + assertEquals("foo-bar-/myApp", response.getContentAsString()); + } + + @Test + public void typeNestedSetBinding() throws Exception { + initDispatcherServlet(NestedSetController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext context) { + RootBeanDefinition csDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class); + csDef.getPropertyValues().add("converters", new TestBeanConverter()); + RootBeanDefinition wbiDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class); + wbiDef.getPropertyValues().add("conversionService", csDef); + RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerMethodAdapter.class); + adapterDef.getPropertyValues().add("webBindingInitializer", wbiDef); + context.registerBeanDefinition("handlerAdapter", adapterDef); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); + request.addParameter("testBeanSet", new String[] {"1", "2"}); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("[1, 2]-org.springframework.beans.TestBean", response.getContentAsString()); + } + + @Test + public void methodNotAllowed() throws Exception { + initDispatcherServlet(MethodNotAllowedController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Invalid response status", HttpServletResponse.SC_METHOD_NOT_ALLOWED, response.getStatus()); + String allowHeader = (String) response.getHeader("Allow"); + assertNotNull("No Allow header", allowHeader); + Set allowedMethods = new HashSet(); + allowedMethods.addAll(Arrays.asList(StringUtils.delimitedListToStringArray(allowHeader, ", "))); + assertEquals("Invalid amount of supported methods", 6, allowedMethods.size()); + assertTrue("PUT not allowed", allowedMethods.contains("PUT")); + assertTrue("DELETE not allowed", allowedMethods.contains("DELETE")); + assertTrue("HEAD not allowed", allowedMethods.contains("HEAD")); + assertTrue("TRACE not allowed", allowedMethods.contains("TRACE")); + assertTrue("OPTIONS not allowed", allowedMethods.contains("OPTIONS")); + assertTrue("POST not allowed", allowedMethods.contains("POST")); + } + + @Test + public void emptyParameterListHandleMethod() throws Exception { + initDispatcherServlet(EmptyParameterListHandlerMethodController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext context) { + RootBeanDefinition vrDef = new RootBeanDefinition(InternalResourceViewResolver.class); + vrDef.getPropertyValues().add("suffix", ".jsp"); + context.registerBeanDefinition("viewResolver", vrDef); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/emptyParameterListHandler"); + MockHttpServletResponse response = new MockHttpServletResponse(); + + EmptyParameterListHandlerMethodController.called = false; + servlet.service(request, response); + assertTrue(EmptyParameterListHandlerMethodController.called); + assertEquals("", response.getContentAsString()); + } + + @SuppressWarnings("rawtypes") + @Test + public void sessionAttributeExposure() throws Exception { + initDispatcherServlet(MySessionAttributesController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext context) { + context.registerBeanDefinition("viewResolver", new RootBeanDefinition(ModelExposingViewResolver.class)); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPage"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("page1", request.getAttribute("viewName")); + HttpSession session = request.getSession(); + assertTrue(session.getAttribute("object1") != null); + assertTrue(session.getAttribute("object2") != null); + assertTrue(((Map) session.getAttribute("model")).containsKey("object1")); + assertTrue(((Map) session.getAttribute("model")).containsKey("object2")); + + request = new MockHttpServletRequest("POST", "/myPage"); + request.setSession(session); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("page2", request.getAttribute("viewName")); + assertTrue(session.getAttribute("object1") != null); + assertTrue(session.getAttribute("object2") != null); + assertTrue(((Map) session.getAttribute("model")).containsKey("object1")); + assertTrue(((Map) session.getAttribute("model")).containsKey("object2")); + } + + @SuppressWarnings("rawtypes") + @Test + public void sessionAttributeExposureWithInterface() throws Exception { + initDispatcherServlet(MySessionAttributesControllerImpl.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext context) { + context.registerBeanDefinition("viewResolver", new RootBeanDefinition(ModelExposingViewResolver.class)); + DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); + autoProxyCreator.setBeanFactory(context.getBeanFactory()); + context.getBeanFactory().addBeanPostProcessor(autoProxyCreator); + context.getBeanFactory().registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPage"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("page1", request.getAttribute("viewName")); + HttpSession session = request.getSession(); + assertTrue(session.getAttribute("object1") != null); + assertTrue(session.getAttribute("object2") != null); + assertTrue(((Map) session.getAttribute("model")).containsKey("object1")); + assertTrue(((Map) session.getAttribute("model")).containsKey("object2")); + + request = new MockHttpServletRequest("POST", "/myPage"); + request.setSession(session); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("page2", request.getAttribute("viewName")); + assertTrue(session.getAttribute("object1") != null); + assertTrue(session.getAttribute("object2") != null); + assertTrue(((Map) session.getAttribute("model")).containsKey("object1")); + assertTrue(((Map) session.getAttribute("model")).containsKey("object2")); + } + + @SuppressWarnings("rawtypes") + @Test + public void parameterizedAnnotatedInterface() throws Exception { + initDispatcherServlet(MyParameterizedControllerImpl.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext context) { + context.registerBeanDefinition("viewResolver", new RootBeanDefinition(ModelExposingViewResolver.class)); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPage"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("page1", request.getAttribute("viewName")); + HttpSession session = request.getSession(); + assertTrue(session.getAttribute("object1") != null); + assertTrue(session.getAttribute("object2") != null); + assertTrue(((Map) session.getAttribute("model")).containsKey("object1")); + assertTrue(((Map) session.getAttribute("model")).containsKey("object2")); + assertTrue(((Map) session.getAttribute("model")).containsKey("testBeanList")); + + request = new MockHttpServletRequest("POST", "/myPage"); + request.setSession(session); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("page2", request.getAttribute("viewName")); + assertTrue(session.getAttribute("object1") != null); + assertTrue(session.getAttribute("object2") != null); + assertTrue(((Map) session.getAttribute("model")).containsKey("object1")); + assertTrue(((Map) session.getAttribute("model")).containsKey("object2")); + assertTrue(((Map) session.getAttribute("model")).containsKey("testBeanList")); + } + + @SuppressWarnings("rawtypes") + @Test + public void parameterizedAnnotatedInterfaceWithOverriddenMappingsInImpl() throws Exception { + initDispatcherServlet(MyParameterizedControllerImplWithOverriddenMappings.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext context) { + context.registerBeanDefinition("viewResolver", new RootBeanDefinition(ModelExposingViewResolver.class)); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPage"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("page1", request.getAttribute("viewName")); + HttpSession session = request.getSession(); + assertTrue(session.getAttribute("object1") != null); + assertTrue(session.getAttribute("object2") != null); + assertTrue(((Map) session.getAttribute("model")).containsKey("object1")); + assertTrue(((Map) session.getAttribute("model")).containsKey("object2")); + assertTrue(((Map) session.getAttribute("model")).containsKey("testBeanList")); + + request = new MockHttpServletRequest("POST", "/myPage"); + request.setSession(session); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("page2", request.getAttribute("viewName")); + assertTrue(session.getAttribute("object1") != null); + assertTrue(session.getAttribute("object2") != null); + assertTrue(((Map) session.getAttribute("model")).containsKey("object1")); + assertTrue(((Map) session.getAttribute("model")).containsKey("object2")); + assertTrue(((Map) session.getAttribute("model")).containsKey("testBeanList")); + } + + @Test + public void adaptedHandleMethods() throws Exception { + doTestAdaptedHandleMethods(MyAdaptedController.class); + } + + @Test + public void adaptedHandleMethods2() throws Exception { + doTestAdaptedHandleMethods(MyAdaptedController2.class); + } + + @Test + public void adaptedHandleMethods3() throws Exception { + doTestAdaptedHandleMethods(MyAdaptedController3.class); + } + + private void doTestAdaptedHandleMethods(final Class controllerClass) throws Exception { + initDispatcherServlet(controllerClass, null); + + 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"); + request.addHeader("header1", "10"); + request.setCookies(new Cookie("cookie1", "3")); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("test-value1-2-10-3", 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()); + } + + @Test + public void formController() throws Exception { + initDispatcherServlet(MyFormController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class)); + } + }); + + 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()); + } + + @Test + public void modelFormController() throws Exception { + initDispatcherServlet(MyModelFormController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class)); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do"); + request.addParameter("name", "name1"); + request.addParameter("age", "value2"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("myPath-name1-typeMismatch-tb1-myValue-yourValue", response.getContentAsString()); + } + + @Test + public void proxiedFormController() throws Exception { + initDispatcherServlet(MyFormController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + 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())); + } + }); + + 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()); + } + + @Test + public void commandProvidingFormControllerWithCustomEditor() throws Exception { + initDispatcherServlet(MyCommandProvidingFormController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class)); + RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerMethodAdapter.class); + adapterDef.getPropertyValues().add("webBindingInitializer", new MyWebBindingInitializer()); + wac.registerBeanDefinition("handlerAdapter", adapterDef); + } + }); + + 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()); + } + + @Test + public void typedCommandProvidingFormController() throws Exception { + initDispatcherServlet(MyTypedCommandProvidingFormController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class)); + RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerMethodAdapter.class); + adapterDef.getPropertyValues().add("webBindingInitializer", new MyWebBindingInitializer()); + adapterDef.getPropertyValues().add("customArgumentResolver", new MySpecialArgumentResolver()); + wac.registerBeanDefinition("handlerAdapter", adapterDef); + } + }); + + 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()); + } + + @Test + public void binderInitializingCommandProvidingFormController() throws Exception { + initDispatcherServlet(MyBinderInitializingCommandProvidingFormController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class)); + } + }); + + 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()); + } + + @Test + public void specificBinderInitializingCommandProvidingFormController() throws Exception { + initDispatcherServlet(MySpecificBinderInitializingCommandProvidingFormController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class)); + } + }); + + 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()); + } + + @Test + public void parameterDispatchingController() throws Exception { + final MockServletContext servletContext = new MockServletContext(); + final MockServletConfig servletConfig = new MockServletConfig(servletContext); + + initDispatcherServlet(MyParameterDispatchingController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + 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); + } + }); + + 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.getId(), request.getAttribute("sessionId")); + assertSame(request.getRequestURI(), request.getAttribute("requestUri")); + assertSame(request.getLocale(), request.getAttribute("locale")); + + 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.getId(), request.getAttribute("sessionId")); + assertSame(request.getRequestURI(), request.getAttribute("requestUri")); + + 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()); + + MyParameterDispatchingController deserialized = (MyParameterDispatchingController) SerializationTestUtils + .serializeAndDeserialize(servlet.getWebApplicationContext().getBean("controller")); + assertNotNull(deserialized.request); + assertNotNull(deserialized.session); + } + + + + + + + @Test + public void relativePathDispatchingController() throws Exception { + initDispatcherServlet(MyRelativePathDispatchingController.class, null); + 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()); + } + + @Test + public void relativeMethodPathDispatchingController() throws Exception { + initDispatcherServlet(MyRelativeMethodPathDispatchingController.class, null); + 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", "/yourApp/myOther"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("myOtherView", response.getContentAsString()); + + request = new MockHttpServletRequest("GET", "/hisApp/myLang"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("myLangView", response.getContentAsString()); + + request = new MockHttpServletRequest("GET", "/herApp/surprise.do"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("mySurpriseView", response.getContentAsString()); + } + + @Test + public void nullCommandController() throws Exception { + initDispatcherServlet(MyNullCommandController.class, null); + 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()); + } + + @Test + public void equivalentMappingsWithSameMethodName() throws Exception { + initDispatcherServlet(ChildController.class, null); + 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")); + } + } + + @Test + public void pathOrdering() throws ServletException, IOException { + initDispatcherServlet(PathOrderingController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/dir/myPath1.do"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("method1", response.getContentAsString()); + } + + @Test + public void requestBodyResponseBody() throws ServletException, IOException { + initDispatcherServlet(RequestResponseBodyController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something"); + String requestBody = "Hello World"; + request.setContent(requestBody.getBytes("UTF-8")); + request.addHeader("Content-Type", "text/plain; charset=utf-8"); + request.addHeader("Accept", "text/*, */*"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals(200, response.getStatus()); + assertEquals(requestBody, response.getContentAsString()); + } + + @Test + public void responseBodyNoAcceptableMediaType() throws ServletException, IOException { + initDispatcherServlet(RequestResponseBodyController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + RootBeanDefinition converterDef = new RootBeanDefinition(StringHttpMessageConverter.class); + converterDef.getPropertyValues().add("supportedMediaTypes", new MediaType("text", "plain")); + RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerMethodAdapter.class); + StringHttpMessageConverter converter = new StringHttpMessageConverter(); + converter.setSupportedMediaTypes(Collections.singletonList(new MediaType("text", "plain"))); + adapterDef.getPropertyValues().add("messageConverters", converter); + wac.registerBeanDefinition("handlerAdapter", adapterDef); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something"); + String requestBody = "Hello World"; + request.setContent(requestBody.getBytes("UTF-8")); + request.addHeader("Content-Type", "text/plain; charset=utf-8"); + request.addHeader("Accept", "application/pdf, application/msword"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals(406, response.getStatus()); + } + + @Test + public void responseBodyWildCardMediaType() throws ServletException, IOException { + initDispatcherServlet(RequestResponseBodyController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something"); + String requestBody = "Hello World"; + request.setContent(requestBody.getBytes("UTF-8")); + request.addHeader("Content-Type", "text/plain; charset=utf-8"); + request.addHeader("Accept", "*/*"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals(requestBody, response.getContentAsString()); + } + + @Test + public void unsupportedRequestBody() throws ServletException, IOException { + initDispatcherServlet(RequestResponseBodyController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerMethodAdapter.class); + adapterDef.getPropertyValues().add("messageConverters", new ByteArrayHttpMessageConverter()); + wac.registerBeanDefinition("handlerAdapter", adapterDef); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something"); + String requestBody = "Hello World"; + request.setContent(requestBody.getBytes("UTF-8")); + request.addHeader("Content-Type", "application/pdf"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals(415, response.getStatus()); + assertNotNull("No Accept response header set", response.getHeader("Accept")); + } + + @Test + public void responseBodyNoAcceptHeader() throws ServletException, IOException { + initDispatcherServlet(RequestResponseBodyController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something"); + String requestBody = "Hello World"; + request.setContent(requestBody.getBytes("UTF-8")); + request.addHeader("Content-Type", "text/plain; charset=utf-8"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals(200, response.getStatus()); + assertEquals(requestBody, response.getContentAsString()); + } + + @Test + public void badRequestRequestBody() throws ServletException, IOException { + initDispatcherServlet(RequestResponseBodyController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerMethodAdapter.class); + adapterDef.getPropertyValues().add("messageConverters", new NotReadableMessageConverter()); + wac.registerBeanDefinition("handlerAdapter", adapterDef); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something"); + String requestBody = "Hello World"; + request.setContent(requestBody.getBytes("UTF-8")); + request.addHeader("Content-Type", "application/pdf"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Invalid response status code", HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); + } + + @Test + public void httpEntity() throws ServletException, IOException { + initDispatcherServlet(ResponseEntityController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/foo"); + String requestBody = "Hello World"; + request.setContent(requestBody.getBytes("UTF-8")); + request.addHeader("Content-Type", "text/plain; charset=utf-8"); + request.addHeader("Accept", "text/*, */*"); + request.addHeader("MyRequestHeader", "MyValue"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals(201, response.getStatus()); + assertEquals(requestBody, response.getContentAsString()); + assertEquals("MyValue", response.getHeader("MyResponseHeader")); + + request = new MockHttpServletRequest("PUT", "/bar"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("MyValue", response.getHeader("MyResponseHeader")); + assertEquals(404, response.getStatus()); + } + + + /* + * See SPR-6877 + */ + @Test + public void overlappingMesssageConvertersRequestBody() throws ServletException, IOException { + initDispatcherServlet(RequestResponseBodyController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerMethodAdapter.class); + List> messageConverters = new ArrayList>(); + messageConverters.add(new StringHttpMessageConverter()); + messageConverters + .add(new SimpleMessageConverter(new MediaType("application","json"), MediaType.ALL)); + adapterDef.getPropertyValues().add("messageConverters", messageConverters); + wac.registerBeanDefinition("handlerAdapter", adapterDef); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something"); + request.setContent("Hello World".getBytes("UTF-8")); + request.addHeader("Content-Type", "text/plain; charset=utf-8"); + request.addHeader("Accept", "application/json, text/javascript, */*"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Invalid response status code", "application/json", response.getHeader("Content-Type")); + } + + @Test + public void responseBodyVoid() throws ServletException, IOException { + initDispatcherServlet(ResponseBodyVoidController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/something"); + request.addHeader("Accept", "text/*, */*"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals(200, response.getStatus()); + } + + @Test + public void responseBodyArgMismatch() throws ServletException, IOException { + initDispatcherServlet(RequestBodyArgMismatchController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); + marshaller.setClassesToBeBound(A.class, B.class); + try { + marshaller.afterPropertiesSet(); + } + catch (Exception ex) { + throw new BeanCreationException(ex.getMessage(), ex); + } + MarshallingHttpMessageConverter messageConverter = new MarshallingHttpMessageConverter(marshaller); + + RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerMethodAdapter.class); + adapterDef.getPropertyValues().add("messageConverters", messageConverter); + wac.registerBeanDefinition("handlerAdapter", adapterDef); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something"); + String requestBody = ""; + request.setContent(requestBody.getBytes("UTF-8")); + request.addHeader("Content-Type", "application/xml; charset=utf-8"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals(400, response.getStatus()); + } + + + @Test + public void contentTypeHeaders() throws ServletException, IOException { + initDispatcherServlet(ContentTypeHeadersController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("POST", "/something"); + request.addHeader("Content-Type", "application/pdf"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("pdf", response.getContentAsString()); + + request = new MockHttpServletRequest("POST", "/something"); + request.addHeader("Content-Type", "text/html"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("text", response.getContentAsString()); + + request = new MockHttpServletRequest("POST", "/something"); + request.addHeader("Content-Type", "application/xml"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals(404, response.getStatus()); + } + + @Test + public void negatedContentTypeHeaders() throws ServletException, IOException { + initDispatcherServlet(NegatedContentTypeHeadersController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("POST", "/something"); + request.addHeader("Content-Type", "application/pdf"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("pdf", response.getContentAsString()); + + request = new MockHttpServletRequest("POST", "/something"); + request.addHeader("Content-Type", "text/html"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("non-pdf", response.getContentAsString()); + } + + // TODO: uncomment ignore + @Ignore + @Test + public void acceptHeaders() throws ServletException, IOException { + initDispatcherServlet(AcceptHeadersController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/something"); + request.addHeader("Accept", "text/html"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("html", response.getContentAsString()); + + request = new MockHttpServletRequest("GET", "/something"); + request.addHeader("Accept", "application/xml"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("xml", response.getContentAsString()); + + request = new MockHttpServletRequest("GET", "/something"); + request.addHeader("Accept", "application/xml, text/html"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("xml", response.getContentAsString()); + + request = new MockHttpServletRequest("GET", "/something"); + request.addHeader("Accept", "text/html;q=0.9, application/xml"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("xml", response.getContentAsString()); + } + + @Test + public void responseStatus() throws ServletException, IOException { + initDispatcherServlet(ResponseStatusController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/something"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("something", response.getContentAsString()); + assertEquals(201, response.getStatus()); + assertEquals("It's alive!", response.getErrorMessage()); + } + + @Test + public void mavResolver() throws ServletException, IOException { + initDispatcherServlet(ModelAndViewResolverController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerMethodAdapter.class); + adapterDef.getPropertyValues().add("customModelAndViewResolver", new MyModelAndViewResolver()); + wac.registerBeanDefinition("handlerAdapter", adapterDef); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("myValue", response.getContentAsString()); + + } + + @Test + public void bindingCookieValue() throws ServletException, IOException { + initDispatcherServlet(BindingCookieValueController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test"); + request.setCookies(new Cookie("date", "2008-11-18")); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("test-2008", response.getContentAsString()); + } + + @Test + public void ambiguousParams() throws ServletException, IOException { + initDispatcherServlet(AmbiguousParamsController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("noParams", response.getContentAsString()); + + request = new MockHttpServletRequest("GET", "/test"); + request.addParameter("myParam", "42"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("myParam-42", response.getContentAsString()); + } + + @Test + public void bridgeMethods() throws Exception { + initDispatcherServlet(TestControllerImpl.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/method"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + } + + @Test + public void requestParamMap() throws Exception { + initDispatcherServlet(RequestParamMapController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/map"); + request.addParameter("key1", "value1"); + request.addParameter("key2", new String[]{"value21", "value22"}); + MockHttpServletResponse response = new MockHttpServletResponse(); + + servlet.service(request, response); + assertEquals("key1=value1,key2=value21", response.getContentAsString()); + + request.setRequestURI("/multiValueMap"); + response = new MockHttpServletResponse(); + + servlet.service(request, response); + assertEquals("key1=[value1],key2=[value21,value22]", response.getContentAsString()); + } + + @Test + public void requestHeaderMap() throws Exception { + initDispatcherServlet(RequestHeaderMapController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/map"); + request.addHeader("Content-Type", "text/html"); + request.addHeader("Custom-Header", new String[]{"value21", "value22"}); + MockHttpServletResponse response = new MockHttpServletResponse(); + + servlet.service(request, response); + assertEquals("Content-Type=text/html,Custom-Header=value21", response.getContentAsString()); + + request.setRequestURI("/multiValueMap"); + response = new MockHttpServletResponse(); + + servlet.service(request, response); + assertEquals("Content-Type=[text/html],Custom-Header=[value21,value22]", response.getContentAsString()); + + request.setRequestURI("/httpHeaders"); + response = new MockHttpServletResponse(); + + servlet.service(request, response); + assertEquals("Content-Type=[text/html],Custom-Header=[value21,value22]", response.getContentAsString()); + } + + + @Test + public void requestMappingInterface() throws Exception { + initDispatcherServlet(IMyControllerImpl.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/handle"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("handle null", response.getContentAsString()); + + request = new MockHttpServletRequest("GET", "/handle"); + request.addParameter("p", "value"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("handle value", response.getContentAsString()); + } + + @Test + public void requestMappingInterfaceWithProxy() throws Exception { + initDispatcherServlet(IMyControllerImpl.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); + autoProxyCreator.setBeanFactory(wac.getBeanFactory()); + wac.getBeanFactory().addBeanPostProcessor(autoProxyCreator); + wac.getBeanFactory().registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/handle"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("handle null", response.getContentAsString()); + + request = new MockHttpServletRequest("GET", "/handle"); + request.addParameter("p", "value"); + response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("handle value", response.getContentAsString()); + } + + @Test + public void requestMappingBaseClass() throws Exception { + initDispatcherServlet(MyAbstractControllerImpl.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/handle"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("handle", response.getContentAsString()); + + } + + @Test + public void trailingSlash() throws Exception { + initDispatcherServlet(TrailingSlashController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo/"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("templatePath", response.getContentAsString()); + } + + /* + * See SPR-6021 + */ + @Test + public void customMapEditor() throws Exception { + initDispatcherServlet(CustomMapEditorController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/handle"); + request.addParameter("map", "bar"); + MockHttpServletResponse response = new MockHttpServletResponse(); + + servlet.service(request, response); + + assertEquals("test-{foo=bar}", response.getContentAsString()); + } + + @Test + public void multipartFileAsSingleString() throws Exception { + initDispatcherServlet(MultipartController.class, null); + + MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); + request.setRequestURI("/singleString"); + request.addFile(new MockMultipartFile("content", "Juergen".getBytes())); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Juergen", response.getContentAsString()); + } + + @Test + public void regularParameterAsSingleString() throws Exception { + initDispatcherServlet(MultipartController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/singleString"); + request.setMethod("POST"); + request.addParameter("content", "Juergen"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Juergen", response.getContentAsString()); + } + + @Test + public void multipartFileAsStringArray() throws Exception { + initDispatcherServlet(MultipartController.class, null); + + MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); + request.setRequestURI("/stringArray"); + request.addFile(new MockMultipartFile("content", "Juergen".getBytes())); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Juergen", response.getContentAsString()); + } + + @Test + public void regularParameterAsStringArray() throws Exception { + initDispatcherServlet(MultipartController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/stringArray"); + request.setMethod("POST"); + request.addParameter("content", "Juergen"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Juergen", response.getContentAsString()); + } + + @Test + public void multipartFilesAsStringArray() throws Exception { + initDispatcherServlet(MultipartController.class, null); + + MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); + request.setRequestURI("/stringArray"); + request.addFile(new MockMultipartFile("content", "Juergen".getBytes())); + request.addFile(new MockMultipartFile("content", "Eva".getBytes())); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Juergen-Eva", response.getContentAsString()); + } + + @Test + public void regularParametersAsStringArray() throws Exception { + initDispatcherServlet(MultipartController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/stringArray"); + request.setMethod("POST"); + request.addParameter("content", "Juergen"); + request.addParameter("content", "Eva"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Juergen-Eva", response.getContentAsString()); + } + + @Test + public void parameterCsvAsStringArray() throws Exception { + initDispatcherServlet(CsvController.class, new BeanDefinitionRegistrar() { + public void register(GenericWebApplicationContext wac) { + RootBeanDefinition csDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class); + RootBeanDefinition wbiDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class); + wbiDef.getPropertyValues().add("conversionService", csDef); + RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerMethodAdapter.class); + adapterDef.getPropertyValues().add("webBindingInitializer", wbiDef); + wac.registerBeanDefinition("handlerAdapter", adapterDef); + } + }); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/integerArray"); + request.setMethod("POST"); + request.addParameter("content", "1,2"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("1-2", response.getContentAsString()); + } + + @Test + public void testMatchWithoutMethodLevelPath() throws Exception { + initDispatcherServlet(NoPathGetAndM2PostController.class, null); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/t1/m2"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals(405, response.getStatus()); + } + + + /* + * Controllers + */ + + @Controller + static class ControllerWithEmptyValueMapping { + + @RequestMapping("") + public void myPath2(HttpServletResponse response) throws IOException { + throw new IllegalStateException("test"); + } + + @RequestMapping("/bar") + public void myPath3(HttpServletResponse response) throws IOException { + response.getWriter().write("testX"); + } + + @ExceptionHandler + public void myPath2(Exception ex, HttpServletResponse response) throws IOException { + response.getWriter().write(ex.getMessage()); + } + } + + @Controller + 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, + @RequestHeader("header1") long h1, @CookieValue("cookie1") Cookie c1, + HttpServletResponse response) throws IOException { + response.getWriter().write("test-" + p1 + "-" + p2 + "-" + h1 + "-" + c1.getValue()); + } + + @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") + 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, + @RequestHeader("header1") String h1, @CookieValue("cookie1") String c1) throws IOException { + response.getWriter().write("test-" + p1 + "-" + param2 + "-" + h1 + "-" + c1); + } + + @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 + static class MyAdaptedControllerBase { + + @RequestMapping("/myPath2.do") + public void myHandle(@RequestParam("param1") T p1, int param2, @RequestHeader Integer header1, + @CookieValue int cookie1, HttpServletResponse response) throws IOException { + response.getWriter().write("test-" + p1 + "-" + param2 + "-" + header1 + "-" + cookie1); + } + + @InitBinder + public void initBinder(@RequestParam("param1") String p1, @RequestParam(value="paramX", required=false) String px, int param2) { + assertNull(px); + } + + @ModelAttribute + public void modelAttribute(@RequestParam("param1") String p1, @RequestParam(value="paramX", required=false) String px, int param2) { + assertNull(px); + } + } + + @RequestMapping("/*.do") + static class MyAdaptedController3 extends MyAdaptedControllerBase { + + @RequestMapping + public void myHandle(HttpServletRequest request, HttpServletResponse response) throws IOException { + response.getWriter().write("test"); + } + + @Override + public void myHandle(@RequestParam("param1") String p1, int param2, @RequestHeader Integer header1, + @CookieValue int cookie1, HttpServletResponse response) throws IOException { + response.getWriter().write("test-" + p1 + "-" + param2 + "-" + header1 + "-" + cookie1); + } + + @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()); + } + + @Override + @InitBinder + public void initBinder(@RequestParam("param1") String p1, @RequestParam(value="paramX", required=false) String px, int param2) { + assertNull(px); + } + + @Override + @ModelAttribute + public void modelAttribute(@RequestParam("param1") String p1, @RequestParam(value="paramX", required=false) String px, int param2) { + assertNull(px); + } + } + + @Controller + @RequestMapping(method = RequestMethod.GET) + static class EmptyParameterListHandlerMethodController { + + static boolean called; + + @RequestMapping("/emptyParameterListHandler") + public void emptyParameterListHandler() { + EmptyParameterListHandlerMethodController.called = true; + } + + @RequestMapping("/nonEmptyParameterListHandler") + public void nonEmptyParameterListHandler(HttpServletResponse response) { + } + } + + @Controller + @RequestMapping("/myPage") + @SessionAttributes({"object1", "object2"}) + public static class MySessionAttributesController { + + @RequestMapping(method = RequestMethod.GET) + public String get(Model model) { + model.addAttribute("object1", new Object()); + model.addAttribute("object2", new Object()); + return "page1"; + } + + @RequestMapping(method = RequestMethod.POST) + public String post(@ModelAttribute("object1") Object object1) { + //do something with object1 + return "page2"; + + } + } + + @RequestMapping("/myPage") + @SessionAttributes({"object1", "object2"}) + public interface MySessionAttributesControllerIfc { + + @RequestMapping(method = RequestMethod.GET) + String get(Model model); + + @RequestMapping(method = RequestMethod.POST) + String post(@ModelAttribute("object1") Object object1); + } + + @Controller + public static class MySessionAttributesControllerImpl implements MySessionAttributesControllerIfc { + + public String get(Model model) { + model.addAttribute("object1", new Object()); + model.addAttribute("object2", new Object()); + return "page1"; + } + + public String post(@ModelAttribute("object1") Object object1) { + //do something with object1 + return "page2"; + } + } + + @RequestMapping("/myPage") + @SessionAttributes({"object1", "object2"}) + public interface MyParameterizedControllerIfc { + + @ModelAttribute("testBeanList") + List getTestBeans(); + + @RequestMapping(method = RequestMethod.GET) + String get(Model model); + } + + public interface MyEditableParameterizedControllerIfc extends MyParameterizedControllerIfc { + + @RequestMapping(method = RequestMethod.POST) + String post(@ModelAttribute("object1") T object); + } + + @Controller + public static class MyParameterizedControllerImpl implements MyEditableParameterizedControllerIfc { + + public List getTestBeans() { + List list = new LinkedList(); + list.add(new TestBean("tb1")); + list.add(new TestBean("tb2")); + return list; + } + + public String get(Model model) { + model.addAttribute("object1", new TestBean()); + model.addAttribute("object2", new TestBean()); + return "page1"; + } + + public String post(TestBean object) { + //do something with object1 + return "page2"; + } + } + + @Controller + public static class MyParameterizedControllerImplWithOverriddenMappings implements MyEditableParameterizedControllerIfc { + + @ModelAttribute("testBeanList") + public List getTestBeans() { + List list = new LinkedList(); + list.add(new TestBean("tb1")); + list.add(new TestBean("tb2")); + return list; + } + + @RequestMapping(method = RequestMethod.GET) + public String get(Model model) { + model.addAttribute("object1", new TestBean()); + model.addAttribute("object2", new TestBean()); + return "page1"; + } + + @RequestMapping(method = RequestMethod.POST) + public String post(@ModelAttribute("object1") TestBean object1) { + //do something with object1 + return "page2"; + } + } + + @Controller + public static class MyFormController { + + @ModelAttribute("testBeanList") + public List getTestBeans() { + List list = new LinkedList(); + 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) { + FieldError error = errors.getFieldError("age"); + assertNotNull("Must have field error for age property", error); + assertEquals("value2", error.getRejectedValue()); + if (!model.containsKey("myKey")) { + model.addAttribute("myKey", "myValue"); + } + return "myView"; + } + } + + public static class ValidTestBean extends TestBean { + + @NotNull + private String validCountry; + + public void setValidCountry(String validCountry) { + this.validCountry = validCountry; + } + + public String getValidCountry() { + return this.validCountry; + } + } + + @Controller + public static class MyModelFormController { + + @ModelAttribute + public List getTestBeans() { + List list = new LinkedList(); + list.add(new TestBean("tb1")); + list.add(new TestBean("tb2")); + return list; + } + + @RequestMapping("/myPath.do") + @ModelAttribute("yourKey") + public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, Model model) { + if (!model.containsAttribute("myKey")) { + model.addAttribute("myKey", "myValue"); + } + return "yourValue"; + } + } + + @Controller + static class MyCommandProvidingFormController extends MyFormController { + + @SuppressWarnings("unused") + @ModelAttribute("myCommand") + private ValidTestBean createTestBean(@RequestParam T defaultName, + Map model, + @RequestParam Date date) { + model.put("myKey", "myOriginalValue"); + ValidTestBean tb = new ValidTestBean(); + tb.setName(defaultName.getClass().getSimpleName() + ":" + defaultName.toString()); + return tb; + } + + @Override + @RequestMapping("/myPath.do") + public String myHandle(@ModelAttribute("myCommand") @Valid TestBean tb, BindingResult errors, ModelMap model) { + if (!errors.hasFieldErrors("validCountry")) { + throw new IllegalStateException("Declarative validation not applied"); + } + 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"; + } + + @SuppressWarnings("unchecked") + @ModelAttribute + protected TB2 getModelAttr() { + return (TB2) new DerivedTestBean(); + } + } + + static class MySpecialArg { + + public MySpecialArg(String value) { + } + } + + @Controller + static class MyTypedCommandProvidingFormController + extends MyCommandProvidingFormController { + + } + + @Controller + static class MyBinderInitializingCommandProvidingFormController + extends MyCommandProvidingFormController { + + @SuppressWarnings("unused") + @InitBinder + private void initBinder(WebDataBinder binder) { + binder.initBeanPropertyAccess(); + binder.setRequiredFields("sex"); + LocalValidatorFactoryBean vf = new LocalValidatorFactoryBean(); + vf.afterPropertiesSet(); + binder.setValidator(vf); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + dateFormat.setLenient(false); + binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); + } + + @Override + @RequestMapping("/myPath.do") + public String myHandle(@ModelAttribute("myCommand") @Valid TestBean tb, BindingResult errors, ModelMap model) { + if (!errors.hasFieldErrors("sex")) { + throw new IllegalStateException("requiredFields not applied"); + } + return super.myHandle(tb, errors, model); + } + } + + @Controller + static class MySpecificBinderInitializingCommandProvidingFormController + extends MyCommandProvidingFormController { + + @SuppressWarnings("unused") + @InitBinder({"myCommand", "date"}) + private void initBinder(WebDataBinder binder, String date, @RequestParam("date") String[] date2) { + LocalValidatorFactoryBean vf = new LocalValidatorFactoryBean(); + vf.afterPropertiesSet(); + binder.setValidator(vf); + 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)); + } + } + + static class MyWebBindingInitializer implements WebBindingInitializer { + + public void initBinder(WebDataBinder binder, WebRequest request) { + LocalValidatorFactoryBean vf = new LocalValidatorFactoryBean(); + vf.afterPropertiesSet(); + binder.setValidator(vf); + assertNotNull(request.getLocale()); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + dateFormat.setLenient(false); + binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); + } + } + + 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") + static class MyParameterDispatchingController implements Serializable { + + private static final long serialVersionUID = 1L; + + @Autowired + private transient ServletContext servletContext; + + @Autowired + private transient ServletConfig servletConfig; + + @Autowired + private HttpSession session; + + @Autowired + private HttpServletRequest request; + + @Autowired + private WebRequest webRequest; + + @RequestMapping + public void myHandle(HttpServletResponse response, HttpServletRequest request) throws IOException { + if (this.servletContext == null || this.servletConfig == null || this.session == null || + this.request == null || this.webRequest == null) { + throw new IllegalStateException(); + } + response.getWriter().write("myView"); + request.setAttribute("servletContext", this.servletContext); + request.setAttribute("servletConfig", this.servletConfig); + request.setAttribute("sessionId", this.session.getId()); + request.setAttribute("requestUri", this.request.getRequestURI()); + request.setAttribute("locale", this.webRequest.getLocale()); + } + + @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 = "/myPath.do", params = {"active"}) + static class MyConstrainedParameterDispatchingController { + + @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"); + } + } + + @Controller + @RequestMapping("/myApp/*") + 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 + static class MyRelativeMethodPathDispatchingController { + + @RequestMapping("**/myHandle") + 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 + 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"); + } + } + + static class TestPrincipal implements Principal { + + public String getName() { + return "test"; + } + } + + static class OtherPrincipal implements Principal { + + public String getName() { + return "other"; + } + } + + 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", "rawtypes"}) + 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() != null && tb.getName().endsWith("myDefaultName")) { + assertEquals(107, tb.getDate().getYear()); + } + 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 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") + + (model.containsKey("yourKey") ? "-" + model.get("yourKey") : "")); + } + else { + response.getWriter().write(viewName + "-" + tb.getName() + "-" + tb.getAge() + "-" + + errors.getFieldValue("name") + "-" + errors.getFieldValue("age")); + } + } + }; + } + } + + public static class ModelExposingViewResolver implements ViewResolver { + + public View resolveViewName(final String viewName, Locale locale) throws Exception { + return new View() { + public String getContentType() { + return null; + } + public void render(Map model, HttpServletRequest request, HttpServletResponse response) { + request.setAttribute("viewName", viewName); + request.getSession().setAttribute("model", model); + } + }; + } + } + + 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) { + } + } + + @Target({ElementType.TYPE}) + @Retention(RetentionPolicy.RUNTIME) + @Controller + public @interface MyControllerAnnotation { + + } + + @MyControllerAnnotation + public static class CustomAnnotationController { + + @RequestMapping("/myPath.do") + public void myHandle() { + } + } + + @Controller + public static class RequiredParamController { + + @RequestMapping("/myPath.do") + public void myHandle(@RequestParam(value = "id", required = true) int id, + @RequestHeader(value = "header", required = true) String header) { + } + } + + @Controller + public static class OptionalParamController { + + @RequestMapping("/myPath.do") + public void myHandle(@RequestParam(required = false) String id, + @RequestParam(required = false) boolean flag, + @RequestHeader(value = "header", required = false) String header, + HttpServletResponse response) throws IOException { + response.getWriter().write(String.valueOf(id) + "-" + flag + "-" + String.valueOf(header)); + } + } + + @Controller + public static class DefaultValueParamController { + + @RequestMapping("/myPath.do") + public void myHandle(@RequestParam(value = "id", defaultValue = "foo") String id, + @RequestParam(value = "otherId", defaultValue = "") String id2, + @RequestHeader(defaultValue = "bar") String header, + HttpServletResponse response) throws IOException { + response.getWriter().write(String.valueOf(id) + "-" + String.valueOf(id2) + "-" + String.valueOf(header)); + } + } + + @Controller + public static class DefaultExpressionValueParamController { + + @RequestMapping("/myPath.do") + public void myHandle(@RequestParam(value = "id", defaultValue = "${myKey}") String id, + @RequestHeader(defaultValue = "#{systemProperties.myHeader}") String header, + @Value("#{request.contextPath}") String contextPath, + HttpServletResponse response) throws IOException { + response.getWriter().write(String.valueOf(id) + "-" + String.valueOf(header) + "-" + contextPath); + } + } + + @Controller + public static class NestedSetController { + + @RequestMapping("/myPath.do") + public void myHandle(GenericBean gb, HttpServletResponse response) throws Exception { + response.getWriter().write(gb.getTestBeanSet().toString() + "-" + + gb.getTestBeanSet().iterator().next().getClass().getName()); + } + } + + public static class TestBeanConverter implements Converter { + + public ITestBean convert(String source) { + return new TestBean(source); + } + } + + @Controller + public static class MethodNotAllowedController { + + @RequestMapping(value = "/myPath.do", method = RequestMethod.DELETE) + public void delete() { + } + + @RequestMapping(value = "/myPath.do", method = RequestMethod.HEAD) + public void head() { + } + + @RequestMapping(value = "/myPath.do", method = RequestMethod.OPTIONS) + public void options() { + } + + @RequestMapping(value = "/myPath.do", method = RequestMethod.POST) + public void post() { + } + + @RequestMapping(value = "/myPath.do", method = RequestMethod.PUT) + public void put() { + } + + @RequestMapping(value = "/myPath.do", method = RequestMethod.TRACE) + public void trace() { + } + + @RequestMapping(value = "/otherPath.do", method = RequestMethod.GET) + public void get() { + } + } + + @Controller + public static class PathOrderingController { + + @RequestMapping(value = {"/dir/myPath1.do", "/**/*.do"}) + public void method1(Writer writer) throws IOException { + writer.write("method1"); + } + + @RequestMapping("/dir/*.do") + public void method2(Writer writer) throws IOException { + writer.write("method2"); + } + } + + @Controller + public static class RequestResponseBodyController { + + @RequestMapping(value = "/something", method = RequestMethod.PUT) + @ResponseBody + public String handle(@RequestBody String body) throws IOException { + return body; + } + } + + @Controller + public static class ResponseBodyVoidController { + + @RequestMapping("/something") + @ResponseBody + public void handle() throws IOException { + } + } + + @Controller + public static class RequestBodyArgMismatchController { + + @RequestMapping(value = "/something", method = RequestMethod.PUT) + public void handle(@RequestBody A a) throws IOException { + } + } + + @XmlRootElement + public static class A { + + } + + @XmlRootElement + public static class B { + + } + + + public static class NotReadableMessageConverter implements HttpMessageConverter { + + public boolean canRead(Class clazz, MediaType mediaType) { + return true; + } + + public boolean canWrite(Class clazz, MediaType mediaType) { + return true; + } + + public List getSupportedMediaTypes() { + return Collections.singletonList(new MediaType("application", "pdf")); + } + + public Object read(Class clazz, HttpInputMessage inputMessage) + throws IOException, HttpMessageNotReadableException { + throw new HttpMessageNotReadableException("Could not read"); + } + + public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) + throws IOException, HttpMessageNotWritableException { + throw new UnsupportedOperationException("Not implemented"); + } + } + + public static class SimpleMessageConverter implements HttpMessageConverter { + + private final List supportedMediaTypes; + + public SimpleMessageConverter(MediaType... supportedMediaTypes) { + this.supportedMediaTypes = Arrays.asList(supportedMediaTypes); + } + + public boolean canRead(Class clazz, MediaType mediaType) { + return supportedMediaTypes.contains(mediaType); + } + + public boolean canWrite(Class clazz, MediaType mediaType) { + return supportedMediaTypes.contains(mediaType); + } + + public List getSupportedMediaTypes() { + return supportedMediaTypes; + } + + public Object read(Class clazz, HttpInputMessage inputMessage) + throws IOException, HttpMessageNotReadableException { + return null; + } + + public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) + throws IOException, HttpMessageNotWritableException { + outputMessage.getHeaders().setContentType(contentType); + outputMessage.getBody(); // force a header write + } + } + + @Controller + public static class ContentTypeHeadersController { + + @RequestMapping(value = "/something", headers = "content-type=application/pdf") + public void handlePdf(Writer writer) throws IOException { + writer.write("pdf"); + } + + @RequestMapping(value = "/something", headers = "content-type=text/*") + public void handleHtml(Writer writer) throws IOException { + writer.write("text"); + } + } + + @Controller + public static class NegatedContentTypeHeadersController { + + @RequestMapping(value = "/something", headers = "content-type=application/pdf") + public void handlePdf(Writer writer) throws IOException { + writer.write("pdf"); + } + + @RequestMapping(value = "/something", headers = "content-type!=application/pdf") + public void handleNonPdf(Writer writer) throws IOException { + writer.write("non-pdf"); + } + + } + + @Controller + public static class AcceptHeadersController { + + @RequestMapping(value = "/something", headers = "accept=text/html") + public void handleHtml(Writer writer) throws IOException { + writer.write("html"); + } + + @RequestMapping(value = "/something", headers = "accept=application/xml") + public void handleXml(Writer writer) throws IOException { + writer.write("xml"); + } + } + + @Controller + public static class ResponseStatusController { + + @RequestMapping("/something") + @ResponseStatus(value = HttpStatus.CREATED, reason = "It's alive!") + public void handle(Writer writer) throws IOException { + writer.write("something"); + } + } + + @Controller + public static class ModelAndViewResolverController { + + @RequestMapping("/") + public MySpecialArg handle() { + return new MySpecialArg("foo"); + } + } + + public static class MyModelAndViewResolver implements ModelAndViewResolver { + + @SuppressWarnings("rawtypes") + public ModelAndView resolveModelAndView(Method handlerMethod, + Class handlerType, + Object returnValue, + ExtendedModelMap implicitModel, + NativeWebRequest webRequest) { + if (returnValue instanceof MySpecialArg) { + return new ModelAndView(new View() { + public String getContentType() { + return "text/html"; + } + + public void render(Map model, HttpServletRequest request, HttpServletResponse response) + throws Exception { + response.getWriter().write("myValue"); + } + + }); + } + return UNRESOLVED; + } + } + + @Controller + @RequestMapping("/test*") + static class AmbiguousParamsController { + + @RequestMapping(method = RequestMethod.GET) + public void noParams(Writer writer) throws IOException { + writer.write("noParams"); + } + + @RequestMapping(params = "myParam") + public void param(@RequestParam("myParam") int myParam, Writer writer) throws IOException { + writer.write("myParam-" + myParam); + } + } + + @Controller + @RequestMapping("/test*") + public static class BindingCookieValueController { + + @InitBinder + public void initBinder(WebDataBinder binder) { + binder.initBeanPropertyAccess(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + dateFormat.setLenient(false); + binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); + } + + @RequestMapping(method = RequestMethod.GET) + public void handle(@CookieValue("date") Date date, Writer writer) throws IOException { + assertEquals("Invalid path variable value", new GregorianCalendar(2008, 10, 18).getTime(), date); + writer.write("test-" + new SimpleDateFormat("yyyy").format(date)); + } + } + + public interface TestController { + + ModelAndView method(T object); + } + + public static class MyEntity { + + } + + @Controller + public static class TestControllerImpl implements TestController { + + @RequestMapping("/method") + public ModelAndView method(MyEntity object) { + return new ModelAndView("/something"); + } + } + + @Controller + public static class RequestParamMapController { + + @RequestMapping("/map") + public void map(@RequestParam Map params, Writer writer) throws IOException { + for (Iterator> it = params.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + writer.write(entry.getKey() + "=" + entry.getValue()); + if (it.hasNext()) { + writer.write(','); + } + + } + } + + @RequestMapping("/multiValueMap") + public void multiValueMap(@RequestParam MultiValueMap params, Writer writer) + throws IOException { + for (Iterator>> it1 = params.entrySet().iterator(); it1.hasNext();) { + Map.Entry> entry = it1.next(); + writer.write(entry.getKey() + "=["); + for (Iterator it2 = entry.getValue().iterator(); it2.hasNext();) { + String value = it2.next(); + writer.write(value); + if (it2.hasNext()) { + writer.write(','); + } + } + writer.write(']'); + if (it1.hasNext()) { + writer.write(','); + } + } + } + } + + @Controller + public static class RequestHeaderMapController { + + @RequestMapping("/map") + public void map(@RequestHeader Map headers, Writer writer) throws IOException { + for (Iterator> it = headers.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + writer.write(entry.getKey() + "=" + entry.getValue()); + if (it.hasNext()) { + writer.write(','); + } + + } + } + + @RequestMapping("/multiValueMap") + public void multiValueMap(@RequestHeader MultiValueMap headers, Writer writer) + throws IOException { + for (Iterator>> it1 = headers.entrySet().iterator(); it1.hasNext();) { + Map.Entry> entry = it1.next(); + writer.write(entry.getKey() + "=["); + for (Iterator it2 = entry.getValue().iterator(); it2.hasNext();) { + String value = it2.next(); + writer.write(value); + if (it2.hasNext()) { + writer.write(','); + } + } + writer.write(']'); + if (it1.hasNext()) { + writer.write(','); + } + } + } + + @RequestMapping("/httpHeaders") + public void httpHeaders(@RequestHeader HttpHeaders headers, Writer writer) throws IOException { + assertEquals("Invalid Content-Type", new MediaType("text", "html"), headers.getContentType()); + multiValueMap(headers, writer); + } + + } + + @Controller + public interface IMyController { + + @RequestMapping("/handle") + void handle(Writer writer, @RequestParam(value="p", required=false) String param) throws IOException; + } + + @Controller + public static class IMyControllerImpl implements IMyController { + + public void handle(Writer writer, @RequestParam(value="p", required=false) String param) throws IOException { + writer.write("handle " + param); + } + } + + public static abstract class MyAbstractController { + + @RequestMapping("/handle") + public abstract void handle(Writer writer) throws IOException; + } + + @Controller + public static class MyAbstractControllerImpl extends MyAbstractController { + + @Override + public void handle(Writer writer) throws IOException { + writer.write("handle"); + } + } + + @Controller + public static class TrailingSlashController { + + @RequestMapping(value = "/", method = RequestMethod.GET) + public void root(Writer writer) throws IOException { + writer.write("root"); + } + + @RequestMapping(value = "/{templatePath}/", method = RequestMethod.GET) + public void templatePath(Writer writer) throws IOException { + writer.write("templatePath"); + } + } + + @Controller + public static class ResponseEntityController { + + @RequestMapping("/foo") + public ResponseEntity foo(HttpEntity requestEntity) throws UnsupportedEncodingException { + assertNotNull(requestEntity); + assertEquals("MyValue", requestEntity.getHeaders().getFirst("MyRequestHeader")); + String requestBody = new String(requestEntity.getBody(), "UTF-8"); + assertEquals("Hello World", requestBody); + + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.set("MyResponseHeader", "MyValue"); + return new ResponseEntity(requestBody, responseHeaders, HttpStatus.CREATED); + } + + @RequestMapping("/bar") + public ResponseEntity bar() { + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.set("MyResponseHeader", "MyValue"); + return new ResponseEntity(responseHeaders, HttpStatus.NOT_FOUND); + } + + } + + @Controller + public static class CustomMapEditorController { + + @InitBinder + public void initBinder(WebDataBinder binder) { + binder.initBeanPropertyAccess(); + binder.registerCustomEditor(Map.class, new CustomMapEditor()); + } + + @SuppressWarnings("rawtypes") + @RequestMapping("/handle") + public void handle(@RequestParam("map") Map map, Writer writer) throws IOException { + writer.write("test-" + map); + } + } + + public static class CustomMapEditor extends PropertyEditorSupport { + + @Override + public void setAsText(String text) throws IllegalArgumentException { + if (StringUtils.hasText(text)) { + setValue(Collections.singletonMap("foo", text)); + } + else { + setValue(null); + } + } + + } + + @Controller + public static class MultipartController { + + @InitBinder + public void initBinder(WebDataBinder binder) { + binder.registerCustomEditor(String.class, new StringMultipartFileEditor()); + } + + @RequestMapping("/singleString") + public void processMultipart(@RequestParam("content") String content, HttpServletResponse response) throws IOException { + response.getWriter().write(content); + } + + @RequestMapping("/stringArray") + public void processMultipart(@RequestParam("content") String[] content, HttpServletResponse response) throws IOException { + response.getWriter().write(StringUtils.arrayToDelimitedString(content, "-")); + } + } + + @Controller + public static class CsvController { + + @RequestMapping("/singleInteger") + public void processCsv(@RequestParam("content") Integer content, HttpServletResponse response) throws IOException { + response.getWriter().write(content.toString()); + } + + @RequestMapping("/integerArray") + public void processCsv(@RequestParam("content") Integer[] content, HttpServletResponse response) throws IOException { + response.getWriter().write(StringUtils.arrayToDelimitedString(content, "-")); + } + } + + @Controller + @RequestMapping("/t1") + protected static class NoPathGetAndM2PostController { + @RequestMapping(method = RequestMethod.GET) + public void handle1(Writer writer) throws IOException { + writer.write("handle1"); + } + + @RequestMapping(value = "/m2", method = RequestMethod.POST) + public void handle2(Writer writer) throws IOException { + writer.write("handle2"); + } + } + + private interface BeanDefinitionRegistrar { + public void register(GenericWebApplicationContext context); + } + + @SuppressWarnings("serial") + private void initDispatcherServlet(final Class controllerClass, final BeanDefinitionRegistrar registrar) + throws ServletException { + + servlet = new DispatcherServlet() { + @Override + protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerClass)); + + Class mappingType = RequestMappingHandlerMethodMapping.class; + wac.registerBeanDefinition("handlerMapping", new RootBeanDefinition(mappingType)); + + Class adapterType = RequestMappingHandlerMethodAdapter.class; + wac.registerBeanDefinition("handlerAdapter", new RootBeanDefinition(adapterType)); + + Class resolverType = RequestMappingHandlerMethodExceptionResolver.class; + wac.registerBeanDefinition("requestMappingResolver", new RootBeanDefinition(resolverType)); + + resolverType = ResponseStatusExceptionResolver.class; + wac.registerBeanDefinition("responseStatusResolver", new RootBeanDefinition(resolverType)); + + resolverType = DefaultHandlerExceptionResolver.class; + wac.registerBeanDefinition("defaultResolver", new RootBeanDefinition(resolverType)); + + if (registrar != null) { + registrar.register(wac); + } + + wac.refresh(); + return wac; + } + }; + servlet.init(new MockServletConfig()); + } + +// Test cases deleted from the original SevletAnnotationControllerTests: + +// @Ignore("Controller interface => no method-level @RequestMapping annotation") +// public void standardHandleMethod() throws Exception { + +// @Ignore("ControllerClassNameHandlerMapping") +// public void emptyRequestMapping() throws Exception { + +// @Ignore("Controller interface => no method-level @RequestMapping annotation") +// public void proxiedStandardHandleMethod() throws Exception { + +// @Ignore("ServletException no longer thrown for unmatched parameter constraints") +// public void constrainedParameterDispatchingController() throws Exception { + +// @Ignore("Method name dispatching") +// public void methodNameDispatchingController() throws Exception { + +// @Ignore("Method name dispatching") +// public void methodNameDispatchingControllerWithSuffix() throws Exception { + +// @Ignore("ControllerClassNameHandlerMapping") +// public void controllerClassNamePlusMethodNameDispatchingController() throws Exception { + +// @Ignore("Method name dispatching") +// public void postMethodNameDispatchingController() throws Exception { + +// @Ignore("ControllerClassNameHandlerMapping") +// public void controllerClassNameNoTypeLevelAnn() throws Exception { + +// @Ignore("SimpleUrlHandlerMapping") +// public void simpleUrlHandlerMapping() throws Exception { + +}