View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.web.servlet.mvc.method.annotation;
18  
19  import java.beans.PropertyEditorSupport;
20  import java.io.IOException;
21  import java.io.Serializable;
22  import java.io.UnsupportedEncodingException;
23  import java.io.Writer;
24  import java.lang.annotation.ElementType;
25  import java.lang.annotation.Retention;
26  import java.lang.annotation.RetentionPolicy;
27  import java.lang.annotation.Target;
28  import java.lang.reflect.Method;
29  import java.net.URI;
30  import java.net.URISyntaxException;
31  import java.security.Principal;
32  import java.text.SimpleDateFormat;
33  import java.util.ArrayList;
34  import java.util.Arrays;
35  import java.util.Collections;
36  import java.util.Date;
37  import java.util.GregorianCalendar;
38  import java.util.HashSet;
39  import java.util.Iterator;
40  import java.util.LinkedList;
41  import java.util.List;
42  import java.util.Locale;
43  import java.util.Map;
44  import java.util.Set;
45  import javax.servlet.ServletConfig;
46  import javax.servlet.ServletContext;
47  import javax.servlet.ServletException;
48  import javax.servlet.http.Cookie;
49  import javax.servlet.http.HttpServletRequest;
50  import javax.servlet.http.HttpServletResponse;
51  import javax.servlet.http.HttpSession;
52  import javax.validation.Valid;
53  import javax.validation.constraints.NotNull;
54  import javax.xml.bind.annotation.XmlRootElement;
55  
56  import org.junit.Test;
57  
58  import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
59  import org.springframework.aop.interceptor.SimpleTraceInterceptor;
60  import org.springframework.aop.support.DefaultPointcutAdvisor;
61  import org.springframework.beans.factory.BeanCreationException;
62  import org.springframework.beans.factory.annotation.Autowired;
63  import org.springframework.beans.factory.annotation.Value;
64  import org.springframework.beans.factory.config.BeanDefinition;
65  import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
66  import org.springframework.beans.factory.support.RootBeanDefinition;
67  import org.springframework.beans.propertyeditors.CustomDateEditor;
68  import org.springframework.context.ApplicationContextInitializer;
69  import org.springframework.context.annotation.AnnotationConfigUtils;
70  import org.springframework.core.MethodParameter;
71  import org.springframework.core.convert.converter.Converter;
72  import org.springframework.format.support.FormattingConversionServiceFactoryBean;
73  import org.springframework.http.HttpEntity;
74  import org.springframework.http.HttpHeaders;
75  import org.springframework.http.HttpInputMessage;
76  import org.springframework.http.HttpOutputMessage;
77  import org.springframework.http.HttpStatus;
78  import org.springframework.http.MediaType;
79  import org.springframework.http.ResponseEntity;
80  import org.springframework.http.converter.ByteArrayHttpMessageConverter;
81  import org.springframework.http.converter.HttpMessageConverter;
82  import org.springframework.http.converter.HttpMessageNotReadableException;
83  import org.springframework.http.converter.HttpMessageNotWritableException;
84  import org.springframework.http.converter.StringHttpMessageConverter;
85  import org.springframework.http.converter.xml.MarshallingHttpMessageConverter;
86  import org.springframework.mock.web.test.MockHttpServletRequest;
87  import org.springframework.mock.web.test.MockHttpServletResponse;
88  import org.springframework.mock.web.test.MockMultipartFile;
89  import org.springframework.mock.web.test.MockMultipartHttpServletRequest;
90  import org.springframework.mock.web.test.MockServletConfig;
91  import org.springframework.mock.web.test.MockServletContext;
92  import org.springframework.oxm.jaxb.Jaxb2Marshaller;
93  import org.springframework.stereotype.Controller;
94  import org.springframework.tests.sample.beans.DerivedTestBean;
95  import org.springframework.tests.sample.beans.GenericBean;
96  import org.springframework.tests.sample.beans.ITestBean;
97  import org.springframework.tests.sample.beans.TestBean;
98  import org.springframework.ui.ExtendedModelMap;
99  import org.springframework.ui.Model;
100 import org.springframework.ui.ModelMap;
101 import org.springframework.util.MultiValueMap;
102 import org.springframework.util.SerializationTestUtils;
103 import org.springframework.util.StringUtils;
104 import org.springframework.validation.BindingResult;
105 import org.springframework.validation.Errors;
106 import org.springframework.validation.FieldError;
107 import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
108 import org.springframework.web.bind.WebDataBinder;
109 import org.springframework.web.bind.annotation.CookieValue;
110 import org.springframework.web.bind.annotation.ExceptionHandler;
111 import org.springframework.web.bind.annotation.InitBinder;
112 import org.springframework.web.bind.annotation.ModelAttribute;
113 import org.springframework.web.bind.annotation.RequestBody;
114 import org.springframework.web.bind.annotation.RequestHeader;
115 import org.springframework.web.bind.annotation.RequestMapping;
116 import org.springframework.web.bind.annotation.RequestMethod;
117 import org.springframework.web.bind.annotation.RequestParam;
118 import org.springframework.web.bind.annotation.ResponseBody;
119 import org.springframework.web.bind.annotation.ResponseStatus;
120 import org.springframework.web.bind.annotation.RestController;
121 import org.springframework.web.bind.annotation.SessionAttributes;
122 import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
123 import org.springframework.web.bind.support.WebArgumentResolver;
124 import org.springframework.web.bind.support.WebBindingInitializer;
125 import org.springframework.web.context.WebApplicationContext;
126 import org.springframework.web.context.request.NativeWebRequest;
127 import org.springframework.web.context.request.WebRequest;
128 import org.springframework.web.context.support.GenericWebApplicationContext;
129 import org.springframework.web.method.HandlerMethod;
130 import org.springframework.web.method.support.HandlerMethodArgumentResolver;
131 import org.springframework.web.multipart.support.StringMultipartFileEditor;
132 import org.springframework.web.servlet.ModelAndView;
133 import org.springframework.web.servlet.View;
134 import org.springframework.web.servlet.ViewResolver;
135 import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
136 import org.springframework.web.servlet.mvc.support.RedirectAttributes;
137 import org.springframework.web.servlet.support.RequestContextUtils;
138 import org.springframework.web.servlet.view.InternalResourceViewResolver;
139 
140 import static org.junit.Assert.*;
141 
142 /**
143  * The origin of this test class is {@link ServletAnnotationControllerHandlerMethodTests}.
144  *
145  * Tests in this class run against the {@link HandlerMethod} infrastructure:
146  * <ul>
147  * 	<li>RequestMappingHandlerMapping
148  * 	<li>RequestMappingHandlerAdapter
149  * 	<li>ExceptionHandlerExceptionResolver
150  * </ul>
151  *
152  * <p>Rather than against the existing infrastructure:
153  * <ul>
154  * 	<li>DefaultAnnotationHandlerMapping
155  * 	<li>AnnotationMethodHandlerAdapter
156  * 	<li>AnnotationMethodHandlerExceptionResolver
157  * </ul>
158  *
159  * @author Rossen Stoyanchev
160  */
161 public class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandlerMethodTests {
162 
163 	@Test
164 	public void emptyValueMapping() throws Exception {
165 		initServletWithControllers(ControllerWithEmptyValueMapping.class);
166 
167 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
168 		request.setContextPath("/foo");
169 		request.setServletPath("");
170 		MockHttpServletResponse response = new MockHttpServletResponse();
171 		getServlet().service(request, response);
172 		assertEquals("test", response.getContentAsString());
173 	}
174 
175 	@Test
176 	public void customAnnotationController() throws Exception {
177 		initServletWithControllers(CustomAnnotationController.class);
178 
179 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
180 		MockHttpServletResponse response = new MockHttpServletResponse();
181 		getServlet().service(request, response);
182 		assertEquals("Invalid response status code", HttpServletResponse.SC_OK, response.getStatus());
183 	}
184 
185 	@Test
186 	public void requiredParamMissing() throws Exception {
187 		WebApplicationContext webAppContext = initServletWithControllers(RequiredParamController.class);
188 
189 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
190 		MockHttpServletResponse response = new MockHttpServletResponse();
191 		getServlet().service(request, response);
192 		assertEquals("Invalid response status code", HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
193 		assertTrue(webAppContext.isSingleton(RequiredParamController.class.getSimpleName()));
194 	}
195 
196 	@Test
197 	public void typeConversionError() throws Exception {
198 		initServletWithControllers(RequiredParamController.class);
199 
200 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
201 		request.addParameter("id", "foo");
202 		MockHttpServletResponse response = new MockHttpServletResponse();
203 		getServlet().service(request, response);
204 		assertEquals("Invalid response status code", HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
205 	}
206 
207 	@Test
208 	public void optionalParamPresent() throws Exception {
209 		initServletWithControllers(OptionalParamController.class);
210 
211 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
212 		request.addParameter("id", "val");
213 		request.addParameter("flag", "true");
214 		request.addHeader("header", "otherVal");
215 		MockHttpServletResponse response = new MockHttpServletResponse();
216 		getServlet().service(request, response);
217 		assertEquals("val-true-otherVal", response.getContentAsString());
218 	}
219 
220 	@Test
221 	public void optionalParamMissing() throws Exception {
222 		initServletWithControllers(OptionalParamController.class);
223 
224 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
225 		MockHttpServletResponse response = new MockHttpServletResponse();
226 		getServlet().service(request, response);
227 		assertEquals("null-false-null", response.getContentAsString());
228 	}
229 
230 	@Test
231 	public void defaultParameters() throws Exception {
232 		initServletWithControllers(DefaultValueParamController.class);
233 
234 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
235 		MockHttpServletResponse response = new MockHttpServletResponse();
236 		getServlet().service(request, response);
237 		assertEquals("foo--bar", response.getContentAsString());
238 	}
239 
240 	@Test
241 	public void defaultExpressionParameters() throws Exception {
242 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
243 			@Override
244 			public void initialize(GenericWebApplicationContext context) {
245 				RootBeanDefinition ppc = new RootBeanDefinition(PropertyPlaceholderConfigurer.class);
246 				ppc.getPropertyValues().add("properties", "myKey=foo");
247 				context.registerBeanDefinition("ppc", ppc);
248 			}
249 		}, DefaultExpressionValueParamController.class);
250 
251 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myApp/myPath.do");
252 		request.setContextPath("/myApp");
253 		MockHttpServletResponse response = new MockHttpServletResponse();
254 		System.setProperty("myHeader", "bar");
255 		try {
256 			getServlet().service(request, response);
257 		}
258 		finally {
259 			System.clearProperty("myHeader");
260 		}
261 		assertEquals("foo-bar-/myApp", response.getContentAsString());
262 	}
263 
264 	@Test
265 	public void typeNestedSetBinding() throws Exception {
266 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
267 			@Override
268 			public void initialize(GenericWebApplicationContext context) {
269 				RootBeanDefinition csDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class);
270 				csDef.getPropertyValues().add("converters", new TestBeanConverter());
271 				RootBeanDefinition wbiDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
272 				wbiDef.getPropertyValues().add("conversionService", csDef);
273 				RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
274 				adapterDef.getPropertyValues().add("webBindingInitializer", wbiDef);
275 				context.registerBeanDefinition("handlerAdapter", adapterDef);
276 			}
277 		}, NestedSetController.class);
278 
279 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
280 		request.addParameter("testBeanSet", new String[] {"1", "2"});
281 		MockHttpServletResponse response = new MockHttpServletResponse();
282 		getServlet().service(request, response);
283 		assertEquals("[1, 2]-org.springframework.tests.sample.beans.TestBean", response.getContentAsString());
284 	}
285 
286 	@Test
287 	public void methodNotAllowed() throws Exception {
288 		initServletWithControllers(MethodNotAllowedController.class);
289 
290 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
291 		MockHttpServletResponse response = new MockHttpServletResponse();
292 		getServlet().service(request, response);
293 		assertEquals("Invalid response status", HttpServletResponse.SC_METHOD_NOT_ALLOWED, response.getStatus());
294 		String allowHeader = response.getHeader("Allow");
295 		assertNotNull("No Allow header", allowHeader);
296 		Set<String> allowedMethods = new HashSet<String>();
297 		allowedMethods.addAll(Arrays.asList(StringUtils.delimitedListToStringArray(allowHeader, ", ")));
298 		assertEquals("Invalid amount of supported methods", 6, allowedMethods.size());
299 		assertTrue("PUT not allowed", allowedMethods.contains("PUT"));
300 		assertTrue("DELETE not allowed", allowedMethods.contains("DELETE"));
301 		assertTrue("HEAD not allowed", allowedMethods.contains("HEAD"));
302 		assertTrue("TRACE not allowed", allowedMethods.contains("TRACE"));
303 		assertTrue("OPTIONS not allowed", allowedMethods.contains("OPTIONS"));
304 		assertTrue("POST not allowed", allowedMethods.contains("POST"));
305 	}
306 
307 	@Test
308 	public void emptyParameterListHandleMethod() throws Exception {
309 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
310 			@Override
311 			public void initialize(GenericWebApplicationContext context) {
312 				RootBeanDefinition vrDef = new RootBeanDefinition(InternalResourceViewResolver.class);
313 				vrDef.getPropertyValues().add("suffix", ".jsp");
314 				context.registerBeanDefinition("viewResolver", vrDef);
315 			}
316 		}, EmptyParameterListHandlerMethodController.class);
317 
318 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/emptyParameterListHandler");
319 		MockHttpServletResponse response = new MockHttpServletResponse();
320 
321 		EmptyParameterListHandlerMethodController.called = false;
322 		getServlet().service(request, response);
323 		assertTrue(EmptyParameterListHandlerMethodController.called);
324 		assertEquals("", response.getContentAsString());
325 	}
326 
327 	@SuppressWarnings("rawtypes")
328 	@Test
329 	public void sessionAttributeExposure() throws Exception {
330 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
331 			@Override
332 			public void initialize(GenericWebApplicationContext context) {
333 				context.registerBeanDefinition("viewResolver", new RootBeanDefinition(ModelExposingViewResolver.class));
334 			}
335 		}, MySessionAttributesController.class);
336 
337 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPage");
338 		MockHttpServletResponse response = new MockHttpServletResponse();
339 		getServlet().service(request, response);
340 		assertEquals("page1", request.getAttribute("viewName"));
341 		HttpSession session = request.getSession();
342 		assertTrue(session.getAttribute("object1") != null);
343 		assertTrue(session.getAttribute("object2") != null);
344 		assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
345 		assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
346 
347 		request = new MockHttpServletRequest("POST", "/myPage");
348 		request.setSession(session);
349 		response = new MockHttpServletResponse();
350 		getServlet().service(request, response);
351 		assertEquals("page2", request.getAttribute("viewName"));
352 		assertTrue(session.getAttribute("object1") != null);
353 		assertTrue(session.getAttribute("object2") != null);
354 		assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
355 		assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
356 	}
357 
358 	@SuppressWarnings("rawtypes")
359 	@Test
360 	public void sessionAttributeExposureWithInterface() throws Exception {
361 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
362 			@Override
363 			public void initialize(GenericWebApplicationContext context) {
364 				context.registerBeanDefinition("viewResolver", new RootBeanDefinition(ModelExposingViewResolver.class));
365 				DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
366 				autoProxyCreator.setBeanFactory(context.getBeanFactory());
367 				context.getBeanFactory().addBeanPostProcessor(autoProxyCreator);
368 				context.getBeanFactory().registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
369 			}
370 		}, MySessionAttributesControllerImpl.class);
371 
372 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPage");
373 		MockHttpServletResponse response = new MockHttpServletResponse();
374 		getServlet().service(request, response);
375 		assertEquals("page1", request.getAttribute("viewName"));
376 		HttpSession session = request.getSession();
377 		assertTrue(session.getAttribute("object1") != null);
378 		assertTrue(session.getAttribute("object2") != null);
379 		assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
380 		assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
381 
382 		request = new MockHttpServletRequest("POST", "/myPage");
383 		request.setSession(session);
384 		response = new MockHttpServletResponse();
385 		getServlet().service(request, response);
386 		assertEquals("page2", request.getAttribute("viewName"));
387 		assertTrue(session.getAttribute("object1") != null);
388 		assertTrue(session.getAttribute("object2") != null);
389 		assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
390 		assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
391 	}
392 
393 	@SuppressWarnings("rawtypes")
394 	@Test
395 	public void parameterizedAnnotatedInterface() throws Exception {
396 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
397 			@Override
398 			public void initialize(GenericWebApplicationContext context) {
399 				context.registerBeanDefinition("viewResolver", new RootBeanDefinition(ModelExposingViewResolver.class));
400 			}
401 		}, MyParameterizedControllerImpl.class);
402 
403 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPage");
404 		MockHttpServletResponse response = new MockHttpServletResponse();
405 		getServlet().service(request, response);
406 		assertEquals("page1", request.getAttribute("viewName"));
407 		HttpSession session = request.getSession();
408 		assertTrue(session.getAttribute("object1") != null);
409 		assertTrue(session.getAttribute("object2") != null);
410 		assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
411 		assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
412 		assertTrue(((Map) session.getAttribute("model")).containsKey("testBeanList"));
413 
414 		request = new MockHttpServletRequest("POST", "/myPage");
415 		request.setSession(session);
416 		response = new MockHttpServletResponse();
417 		getServlet().service(request, response);
418 		assertEquals("page2", request.getAttribute("viewName"));
419 		assertTrue(session.getAttribute("object1") != null);
420 		assertTrue(session.getAttribute("object2") != null);
421 		assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
422 		assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
423 		assertTrue(((Map) session.getAttribute("model")).containsKey("testBeanList"));
424 	}
425 
426 	@SuppressWarnings("rawtypes")
427 	@Test
428 	public void parameterizedAnnotatedInterfaceWithOverriddenMappingsInImpl() throws Exception {
429 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
430 			@Override
431 			public void initialize(GenericWebApplicationContext context) {
432 				context.registerBeanDefinition("viewResolver", new RootBeanDefinition(ModelExposingViewResolver.class));
433 			}
434 		}, MyParameterizedControllerImplWithOverriddenMappings.class);
435 
436 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPage");
437 		MockHttpServletResponse response = new MockHttpServletResponse();
438 		getServlet().service(request, response);
439 		assertEquals("page1", request.getAttribute("viewName"));
440 		HttpSession session = request.getSession();
441 		assertTrue(session.getAttribute("object1") != null);
442 		assertTrue(session.getAttribute("object2") != null);
443 		assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
444 		assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
445 		assertTrue(((Map) session.getAttribute("model")).containsKey("testBeanList"));
446 
447 		request = new MockHttpServletRequest("POST", "/myPage");
448 		request.setSession(session);
449 		response = new MockHttpServletResponse();
450 		getServlet().service(request, response);
451 		assertEquals("page2", request.getAttribute("viewName"));
452 		assertTrue(session.getAttribute("object1") != null);
453 		assertTrue(session.getAttribute("object2") != null);
454 		assertTrue(((Map) session.getAttribute("model")).containsKey("object1"));
455 		assertTrue(((Map) session.getAttribute("model")).containsKey("object2"));
456 		assertTrue(((Map) session.getAttribute("model")).containsKey("testBeanList"));
457 	}
458 
459 	@Test
460 	public void adaptedHandleMethods() throws Exception {
461 		doTestAdaptedHandleMethods(MyAdaptedController.class);
462 	}
463 
464 	@Test
465 	public void adaptedHandleMethods2() throws Exception {
466 		doTestAdaptedHandleMethods(MyAdaptedController2.class);
467 	}
468 
469 	@Test
470 	public void adaptedHandleMethods3() throws Exception {
471 		doTestAdaptedHandleMethods(MyAdaptedController3.class);
472 	}
473 
474 	private void doTestAdaptedHandleMethods(final Class<?> controllerClass) throws Exception {
475 		initServletWithControllers(controllerClass);
476 
477 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath1.do");
478 		MockHttpServletResponse response = new MockHttpServletResponse();
479 		request.addParameter("param1", "value1");
480 		request.addParameter("param2", "2");
481 		getServlet().service(request, response);
482 		assertEquals("test", response.getContentAsString());
483 
484 		request = new MockHttpServletRequest("GET", "/myPath2.do");
485 		request.addParameter("param1", "value1");
486 		request.addParameter("param2", "2");
487 		request.addHeader("header1", "10");
488 		request.setCookies(new Cookie("cookie1", "3"));
489 		response = new MockHttpServletResponse();
490 		getServlet().service(request, response);
491 		assertEquals("test-value1-2-10-3", response.getContentAsString());
492 
493 		request = new MockHttpServletRequest("GET", "/myPath3.do");
494 		request.addParameter("param1", "value1");
495 		request.addParameter("param2", "2");
496 		request.addParameter("name", "name1");
497 		request.addParameter("age", "2");
498 		response = new MockHttpServletResponse();
499 		getServlet().service(request, response);
500 		assertEquals("test-name1-2", response.getContentAsString());
501 
502 		request = new MockHttpServletRequest("GET", "/myPath4.do");
503 		request.addParameter("param1", "value1");
504 		request.addParameter("param2", "2");
505 		request.addParameter("name", "name1");
506 		request.addParameter("age", "value2");
507 		response = new MockHttpServletResponse();
508 		getServlet().service(request, response);
509 		assertEquals("test-name1-typeMismatch", response.getContentAsString());
510 	}
511 
512 	@Test
513 	public void formController() throws Exception {
514 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
515 			@Override
516 			public void initialize(GenericWebApplicationContext wac) {
517 				wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class));
518 			}
519 		}, MyFormController.class);
520 
521 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
522 		request.addParameter("name", "name1");
523 		request.addParameter("age", "value2");
524 		MockHttpServletResponse response = new MockHttpServletResponse();
525 		getServlet().service(request, response);
526 		assertEquals("myView-name1-typeMismatch-tb1-myValue", response.getContentAsString());
527 	}
528 
529 	@Test
530 	public void modelFormController() throws Exception {
531 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
532 			@Override
533 			public void initialize(GenericWebApplicationContext wac) {
534 				wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class));
535 			}
536 		}, MyModelFormController.class);
537 
538 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
539 		request.addParameter("name", "name1");
540 		request.addParameter("age", "value2");
541 		MockHttpServletResponse response = new MockHttpServletResponse();
542 		getServlet().service(request, response);
543 		assertEquals("myPath-name1-typeMismatch-tb1-myValue-yourValue", response.getContentAsString());
544 	}
545 
546 	@Test
547 	public void proxiedFormController() throws Exception {
548 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
549 			@Override
550 			public void initialize(GenericWebApplicationContext wac) {
551 				wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class));
552 				DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
553 				autoProxyCreator.setBeanFactory(wac.getBeanFactory());
554 				wac.getBeanFactory().addBeanPostProcessor(autoProxyCreator);
555 				wac.getBeanFactory()
556 						.registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
557 			}
558 		}, MyFormController.class);
559 
560 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
561 		request.addParameter("name", "name1");
562 		request.addParameter("age", "value2");
563 		MockHttpServletResponse response = new MockHttpServletResponse();
564 		getServlet().service(request, response);
565 		assertEquals("myView-name1-typeMismatch-tb1-myValue", response.getContentAsString());
566 	}
567 
568 	@Test
569 	public void commandProvidingFormControllerWithCustomEditor() throws Exception {
570 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
571 			@Override
572 			public void initialize(GenericWebApplicationContext wac) {
573 				wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class));
574 				RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
575 				adapterDef.getPropertyValues().add("webBindingInitializer", new MyWebBindingInitializer());
576 				wac.registerBeanDefinition("handlerAdapter", adapterDef);
577 			}
578 		}, MyCommandProvidingFormController.class);
579 
580 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
581 		request.addParameter("defaultName", "myDefaultName");
582 		request.addParameter("age", "value2");
583 		request.addParameter("date", "2007-10-02");
584 		MockHttpServletResponse response = new MockHttpServletResponse();
585 		getServlet().service(request, response);
586 		assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString());
587 	}
588 
589 	@Test
590 	public void typedCommandProvidingFormController() throws Exception {
591 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
592 			@Override
593 			public void initialize(GenericWebApplicationContext wac) {
594 				wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class));
595 				RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
596 				adapterDef.getPropertyValues().add("webBindingInitializer", new MyWebBindingInitializer());
597 				List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<HandlerMethodArgumentResolver>();
598 				argumentResolvers.add(new ServletWebArgumentResolverAdapter(new MySpecialArgumentResolver()));
599 				adapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
600 				wac.registerBeanDefinition("handlerAdapter", adapterDef);
601 			}
602 		}, MyTypedCommandProvidingFormController.class);
603 
604 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
605 		request.addParameter("defaultName", "10");
606 		request.addParameter("age", "value2");
607 		request.addParameter("date", "2007-10-02");
608 		MockHttpServletResponse response = new MockHttpServletResponse();
609 		getServlet().service(request, response);
610 		assertEquals("myView-Integer:10-typeMismatch-tb1-myOriginalValue", response.getContentAsString());
611 
612 		request = new MockHttpServletRequest("GET", "/myOtherPath.do");
613 		request.addParameter("defaultName", "10");
614 		request.addParameter("age", "value2");
615 		request.addParameter("date", "2007-10-02");
616 		response = new MockHttpServletResponse();
617 		getServlet().service(request, response);
618 		assertEquals("myView-myName-typeMismatch-tb1-myOriginalValue", response.getContentAsString());
619 
620 		request = new MockHttpServletRequest("GET", "/myThirdPath.do");
621 		request.addParameter("defaultName", "10");
622 		request.addParameter("age", "100");
623 		request.addParameter("date", "2007-10-02");
624 		response = new MockHttpServletResponse();
625 		getServlet().service(request, response);
626 		assertEquals("myView-special-99-special-99", response.getContentAsString());
627 	}
628 
629 	@Test
630 	public void binderInitializingCommandProvidingFormController() throws Exception {
631 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
632 			@Override
633 			public void initialize(GenericWebApplicationContext wac) {
634 				wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class));
635 			}
636 		}, MyBinderInitializingCommandProvidingFormController.class);
637 
638 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
639 		request.addParameter("defaultName", "myDefaultName");
640 		request.addParameter("age", "value2");
641 		request.addParameter("date", "2007-10-02");
642 		MockHttpServletResponse response = new MockHttpServletResponse();
643 		getServlet().service(request, response);
644 		assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString());
645 	}
646 
647 	@Test
648 	public void specificBinderInitializingCommandProvidingFormController() throws Exception {
649 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
650 			@Override
651 			public void initialize(GenericWebApplicationContext wac) {
652 				wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class));
653 			}
654 		}, MySpecificBinderInitializingCommandProvidingFormController.class);
655 
656 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
657 		request.addParameter("defaultName", "myDefaultName");
658 		request.addParameter("age", "value2");
659 		request.addParameter("date", "2007-10-02");
660 		MockHttpServletResponse response = new MockHttpServletResponse();
661 		getServlet().service(request, response);
662 		assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString());
663 	}
664 
665 	@Test
666 	public void parameterDispatchingController() throws Exception {
667 		final MockServletContext servletContext = new MockServletContext();
668 		final MockServletConfig servletConfig = new MockServletConfig(servletContext);
669 
670 		WebApplicationContext  webAppContext =
671 			initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
672 				@Override
673 				public void initialize(GenericWebApplicationContext wac) {
674 					wac.setServletContext(servletContext);
675 					AnnotationConfigUtils.registerAnnotationConfigProcessors(wac);
676 					wac.getBeanFactory().registerResolvableDependency(ServletConfig.class, servletConfig);
677 				}
678 		}, MyParameterDispatchingController.class);
679 
680 		MockHttpServletRequest request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do");
681 		MockHttpServletResponse response = new MockHttpServletResponse();
682 		HttpSession session = request.getSession();
683 		getServlet().service(request, response);
684 		assertEquals("myView", response.getContentAsString());
685 		assertSame(servletContext, request.getAttribute("servletContext"));
686 		assertSame(servletConfig, request.getAttribute("servletConfig"));
687 		assertSame(session.getId(), request.getAttribute("sessionId"));
688 		assertSame(request.getRequestURI(), request.getAttribute("requestUri"));
689 		assertSame(request.getLocale(), request.getAttribute("locale"));
690 
691 		request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do");
692 		response = new MockHttpServletResponse();
693 		session = request.getSession();
694 		getServlet().service(request, response);
695 		assertEquals("myView", response.getContentAsString());
696 		assertSame(servletContext, request.getAttribute("servletContext"));
697 		assertSame(servletConfig, request.getAttribute("servletConfig"));
698 		assertSame(session.getId(), request.getAttribute("sessionId"));
699 		assertSame(request.getRequestURI(), request.getAttribute("requestUri"));
700 
701 		request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do");
702 		request.addParameter("view", "other");
703 		response = new MockHttpServletResponse();
704 		getServlet().service(request, response);
705 		assertEquals("myOtherView", response.getContentAsString());
706 
707 		request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do");
708 		request.addParameter("view", "my");
709 		request.addParameter("lang", "de");
710 		response = new MockHttpServletResponse();
711 		getServlet().service(request, response);
712 		assertEquals("myLangView", response.getContentAsString());
713 
714 		request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do");
715 		request.addParameter("surprise", "!");
716 		response = new MockHttpServletResponse();
717 		getServlet().service(request, response);
718 		assertEquals("mySurpriseView", response.getContentAsString());
719 
720 		MyParameterDispatchingController deserialized =
721 			(MyParameterDispatchingController) SerializationTestUtils.serializeAndDeserialize(
722 					webAppContext.getBean(MyParameterDispatchingController.class.getSimpleName()));
723 		assertNotNull(deserialized.request);
724 		assertNotNull(deserialized.session);
725 	}
726 
727 	@Test
728 	public void relativePathDispatchingController() throws Exception {
729 		initServletWithControllers(MyRelativePathDispatchingController.class);
730 		getServlet().init(new MockServletConfig());
731 
732 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myApp/myHandle");
733 		MockHttpServletResponse response = new MockHttpServletResponse();
734 		getServlet().service(request, response);
735 		assertEquals("myView", response.getContentAsString());
736 
737 		request = new MockHttpServletRequest("GET", "/myApp/myOther");
738 		response = new MockHttpServletResponse();
739 		getServlet().service(request, response);
740 		assertEquals("myOtherView", response.getContentAsString());
741 
742 		request = new MockHttpServletRequest("GET", "/myApp/myLang");
743 		response = new MockHttpServletResponse();
744 		getServlet().service(request, response);
745 		assertEquals("myLangView", response.getContentAsString());
746 
747 		request = new MockHttpServletRequest("GET", "/myApp/surprise.do");
748 		response = new MockHttpServletResponse();
749 		getServlet().service(request, response);
750 		assertEquals("mySurpriseView", response.getContentAsString());
751 	}
752 
753 	@Test
754 	public void relativeMethodPathDispatchingController() throws Exception {
755 		initServletWithControllers(MyRelativeMethodPathDispatchingController.class);
756 		getServlet().init(new MockServletConfig());
757 
758 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myApp/myHandle");
759 		MockHttpServletResponse response = new MockHttpServletResponse();
760 		getServlet().service(request, response);
761 		assertEquals("myView", response.getContentAsString());
762 
763 		request = new MockHttpServletRequest("GET", "/yourApp/myOther");
764 		response = new MockHttpServletResponse();
765 		getServlet().service(request, response);
766 		assertEquals("myOtherView", response.getContentAsString());
767 
768 		request = new MockHttpServletRequest("GET", "/hisApp/myLang");
769 		response = new MockHttpServletResponse();
770 		getServlet().service(request, response);
771 		assertEquals("myLangView", response.getContentAsString());
772 
773 		request = new MockHttpServletRequest("GET", "/herApp/surprise.do");
774 		response = new MockHttpServletResponse();
775 		getServlet().service(request, response);
776 		assertEquals("mySurpriseView", response.getContentAsString());
777 	}
778 
779 	@Test
780 	public void nullCommandController() throws Exception {
781 		initServletWithControllers(MyNullCommandController.class);
782 		getServlet().init(new MockServletConfig());
783 
784 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath");
785 		request.setUserPrincipal(new OtherPrincipal());
786 		MockHttpServletResponse response = new MockHttpServletResponse();
787 		getServlet().service(request, response);
788 		assertEquals("myView", response.getContentAsString());
789 	}
790 
791 	@Test
792 	public void equivalentMappingsWithSameMethodName() throws Exception {
793 		try {
794 			initServletWithControllers(ChildController.class);
795 			fail("Expected 'method already mapped' error");
796 		}
797 		catch (BeanCreationException e) {
798 			assertTrue(e.getCause() instanceof IllegalStateException);
799 			assertTrue(e.getCause().getMessage().contains("Ambiguous mapping"));
800 		}
801 	}
802 
803 	@Test
804 	public void pathOrdering() throws ServletException, IOException {
805 		initServletWithControllers(PathOrderingController.class);
806 
807 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/dir/myPath1.do");
808 		MockHttpServletResponse response = new MockHttpServletResponse();
809 		getServlet().service(request, response);
810 		assertEquals("method1", response.getContentAsString());
811 	}
812 
813 	@Test
814 	public void requestBodyResponseBody() throws ServletException, IOException {
815 		initServletWithControllers(RequestResponseBodyController.class);
816 
817 		MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
818 		String requestBody = "Hello World";
819 		request.setContent(requestBody.getBytes("UTF-8"));
820 		request.addHeader("Content-Type", "text/plain; charset=utf-8");
821 		request.addHeader("Accept", "text/*, */*");
822 		MockHttpServletResponse response = new MockHttpServletResponse();
823 		getServlet().service(request, response);
824 		assertEquals(200, response.getStatus());
825 		assertEquals(requestBody, response.getContentAsString());
826 	}
827 
828 	@Test
829 	public void httpPatch() throws ServletException, IOException {
830 		initServletWithControllers(RequestResponseBodyController.class);
831 
832 		MockHttpServletRequest request = new MockHttpServletRequest("PATCH", "/something");
833 		String requestBody = "Hello world!";
834 		request.setContent(requestBody.getBytes("UTF-8"));
835 		request.addHeader("Content-Type", "text/plain; charset=utf-8");
836 		request.addHeader("Accept", "text/*, */*");
837 		MockHttpServletResponse response = new MockHttpServletResponse();
838 		getServlet().service(request, response);
839 		assertEquals(200, response.getStatus());
840 		assertEquals(requestBody, response.getContentAsString());
841 	}
842 
843 	@Test
844 	public void responseBodyNoAcceptableMediaType() throws ServletException, IOException {
845 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
846 			@Override
847 			public void initialize(GenericWebApplicationContext wac) {
848 				RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
849 				StringHttpMessageConverter converter = new StringHttpMessageConverter();
850 				adapterDef.getPropertyValues().add("messageConverters", converter);
851 				wac.registerBeanDefinition("handlerAdapter", adapterDef);
852 			}
853 		}, RequestResponseBodyProducesController.class);
854 
855 		MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
856 		String requestBody = "Hello World";
857 		request.setContent(requestBody.getBytes("UTF-8"));
858 		request.addHeader("Content-Type", "text/plain; charset=utf-8");
859 		request.addHeader("Accept", "application/pdf, application/msword");
860 		MockHttpServletResponse response = new MockHttpServletResponse();
861 		getServlet().service(request, response);
862 		assertEquals(406, response.getStatus());
863 	}
864 
865 	@Test
866 	public void responseBodyWildCardMediaType() throws ServletException, IOException {
867 		initServletWithControllers(RequestResponseBodyController.class);
868 
869 		MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
870 		String requestBody = "Hello World";
871 		request.setContent(requestBody.getBytes("UTF-8"));
872 		request.addHeader("Content-Type", "text/plain; charset=utf-8");
873 		request.addHeader("Accept", "*/*");
874 		MockHttpServletResponse response = new MockHttpServletResponse();
875 		getServlet().service(request, response);
876 		assertEquals(requestBody, response.getContentAsString());
877 	}
878 
879 	@Test
880 	public void unsupportedRequestBody() throws ServletException, IOException {
881 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
882 			@Override
883 			public void initialize(GenericWebApplicationContext wac) {
884 				RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
885 				adapterDef.getPropertyValues().add("messageConverters", new ByteArrayHttpMessageConverter());
886 				wac.registerBeanDefinition("handlerAdapter", adapterDef);
887 			}
888 		}, RequestResponseBodyController.class);
889 
890 		MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
891 		String requestBody = "Hello World";
892 		request.setContent(requestBody.getBytes("UTF-8"));
893 		request.addHeader("Content-Type", "application/pdf");
894 		MockHttpServletResponse response = new MockHttpServletResponse();
895 		getServlet().service(request, response);
896 		assertEquals(415, response.getStatus());
897 		assertNotNull("No Accept response header set", response.getHeader("Accept"));
898 	}
899 
900 	@Test
901 	public void responseBodyNoAcceptHeader() throws ServletException, IOException {
902 		initServletWithControllers(RequestResponseBodyController.class);
903 
904 		MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
905 		String requestBody = "Hello World";
906 		request.setContent(requestBody.getBytes("UTF-8"));
907 		request.addHeader("Content-Type", "text/plain; charset=utf-8");
908 		MockHttpServletResponse response = new MockHttpServletResponse();
909 		getServlet().service(request, response);
910 		assertEquals(200, response.getStatus());
911 		assertEquals(requestBody, response.getContentAsString());
912 	}
913 
914 	@Test
915 	public void badRequestRequestBody() throws ServletException, IOException {
916 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
917 			@Override
918 			public void initialize(GenericWebApplicationContext wac) {
919 				RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
920 				adapterDef.getPropertyValues().add("messageConverters", new NotReadableMessageConverter());
921 				wac.registerBeanDefinition("handlerAdapter", adapterDef);
922 			}
923 		}, RequestResponseBodyController.class);
924 
925 		MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
926 		String requestBody = "Hello World";
927 		request.setContent(requestBody.getBytes("UTF-8"));
928 		request.addHeader("Content-Type", "application/pdf");
929 		MockHttpServletResponse response = new MockHttpServletResponse();
930 		getServlet().service(request, response);
931 		assertEquals("Invalid response status code", HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
932 	}
933 
934 	@Test
935 	public void httpEntity() throws ServletException, IOException {
936 		initServletWithControllers(ResponseEntityController.class);
937 
938 		MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/foo");
939 		String requestBody = "Hello World";
940 		request.setContent(requestBody.getBytes("UTF-8"));
941 		request.addHeader("Content-Type", "text/plain; charset=utf-8");
942 		request.addHeader("Accept", "text/*, */*");
943 		request.addHeader("MyRequestHeader", "MyValue");
944 		MockHttpServletResponse response = new MockHttpServletResponse();
945 		getServlet().service(request, response);
946 		assertEquals(201, response.getStatus());
947 		assertEquals(requestBody, response.getContentAsString());
948 		assertEquals("MyValue", response.getHeader("MyResponseHeader"));
949 
950 		request = new MockHttpServletRequest("PUT", "/bar");
951 		response = new MockHttpServletResponse();
952 		getServlet().service(request, response);
953 		assertEquals("MyValue", response.getHeader("MyResponseHeader"));
954 		assertEquals(404, response.getStatus());
955 	}
956 
957 
958 	/*
959 	 * See SPR-6877
960 	 */
961 	@Test
962 	public void overlappingMessageConvertersRequestBody() throws ServletException, IOException {
963 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
964 			@Override
965 			public void initialize(GenericWebApplicationContext wac) {
966 				RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
967 				List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
968 				messageConverters.add(new StringHttpMessageConverter());
969 				messageConverters
970 						.add(new SimpleMessageConverter(new MediaType("application","json"), MediaType.ALL));
971 				adapterDef.getPropertyValues().add("messageConverters", messageConverters);
972 				wac.registerBeanDefinition("handlerAdapter", adapterDef);
973 			}
974 		}, RequestResponseBodyController.class);
975 
976 		MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
977 		request.setContent("Hello World".getBytes("UTF-8"));
978 		request.addHeader("Content-Type", "text/plain; charset=utf-8");
979 		request.addHeader("Accept", "application/json, text/javascript, */*");
980 		MockHttpServletResponse response = new MockHttpServletResponse();
981 		getServlet().service(request, response);
982 		assertEquals("Invalid content-type", "application/json", response.getHeader("Content-Type"));
983 	}
984 
985 	@Test
986 	public void responseBodyVoid() throws ServletException, IOException {
987 		initServletWithControllers(ResponseBodyVoidController.class);
988 
989 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/something");
990 		request.addHeader("Accept", "text/*, */*");
991 		MockHttpServletResponse response = new MockHttpServletResponse();
992 		getServlet().service(request, response);
993 		assertEquals(200, response.getStatus());
994 	}
995 
996 	@Test
997 	public void responseBodyArgMismatch() throws ServletException, IOException {
998 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
999 			@Override
1000 			public void initialize(GenericWebApplicationContext wac) {
1001 				Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
1002 				marshaller.setClassesToBeBound(A.class, B.class);
1003 				try {
1004 					marshaller.afterPropertiesSet();
1005 				}
1006 				catch (Exception ex) {
1007 					throw new BeanCreationException(ex.getMessage(), ex);
1008 				}
1009 				MarshallingHttpMessageConverter messageConverter = new MarshallingHttpMessageConverter(marshaller);
1010 
1011 				RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
1012 				adapterDef.getPropertyValues().add("messageConverters", messageConverter);
1013 				wac.registerBeanDefinition("handlerAdapter", adapterDef);
1014 			}
1015 		}, RequestBodyArgMismatchController.class);
1016 
1017 		MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
1018 		String requestBody = "<b/>";
1019 		request.setContent(requestBody.getBytes("UTF-8"));
1020 		request.addHeader("Content-Type", "application/xml; charset=utf-8");
1021 		MockHttpServletResponse response = new MockHttpServletResponse();
1022 		getServlet().service(request, response);
1023 		assertEquals(400, response.getStatus());
1024 	}
1025 
1026 
1027 	@Test
1028 	public void contentTypeHeaders() throws ServletException, IOException {
1029 		initServletWithControllers(ContentTypeHeadersController.class);
1030 
1031 		MockHttpServletRequest request = new MockHttpServletRequest("POST", "/something");
1032 		request.setContentType("application/pdf");
1033 		MockHttpServletResponse response = new MockHttpServletResponse();
1034 		getServlet().service(request, response);
1035 		assertEquals("pdf", response.getContentAsString());
1036 
1037 		request = new MockHttpServletRequest("POST", "/something");
1038 		request.setContentType("text/html");
1039 		response = new MockHttpServletResponse();
1040 		getServlet().service(request, response);
1041 		assertEquals("text", response.getContentAsString());
1042 
1043 		request = new MockHttpServletRequest("POST", "/something");
1044 		request.setContentType("application/xml");
1045 		response = new MockHttpServletResponse();
1046 		getServlet().service(request, response);
1047 		assertEquals(415, response.getStatus());
1048 	}
1049 
1050 	@Test
1051 	public void consumes() throws ServletException, IOException {
1052 		initServletWithControllers(ConsumesController.class);
1053 
1054 		MockHttpServletRequest request = new MockHttpServletRequest("POST", "/something");
1055 		request.setContentType("application/pdf");
1056 		MockHttpServletResponse response = new MockHttpServletResponse();
1057 		getServlet().service(request, response);
1058 		assertEquals("pdf", response.getContentAsString());
1059 
1060 		request = new MockHttpServletRequest("POST", "/something");
1061 		request.setContentType("text/html");
1062 		response = new MockHttpServletResponse();
1063 		getServlet().service(request, response);
1064 		assertEquals("text", response.getContentAsString());
1065 
1066 		request = new MockHttpServletRequest("POST", "/something");
1067 		request.setContentType("application/xml");
1068 		response = new MockHttpServletResponse();
1069 		getServlet().service(request, response);
1070 		assertEquals(415, response.getStatus());
1071 	}
1072 
1073 	@Test
1074 	public void negatedContentTypeHeaders() throws ServletException, IOException {
1075 		initServletWithControllers(NegatedContentTypeHeadersController.class);
1076 
1077 		MockHttpServletRequest request = new MockHttpServletRequest("POST", "/something");
1078 		request.setContentType("application/pdf");
1079 		MockHttpServletResponse response = new MockHttpServletResponse();
1080 		getServlet().service(request, response);
1081 		assertEquals("pdf", response.getContentAsString());
1082 
1083 		request = new MockHttpServletRequest("POST", "/something");
1084 		request.setContentType("text/html");
1085 		response = new MockHttpServletResponse();
1086 		getServlet().service(request, response);
1087 		assertEquals("non-pdf", response.getContentAsString());
1088 	}
1089 
1090 	@Test
1091 	public void acceptHeaders() throws ServletException, IOException {
1092 		initServletWithControllers(AcceptHeadersController.class);
1093 
1094 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/something");
1095 		request.addHeader("Accept", "text/html");
1096 		MockHttpServletResponse response = new MockHttpServletResponse();
1097 		getServlet().service(request, response);
1098 		assertEquals("html", response.getContentAsString());
1099 
1100 		request = new MockHttpServletRequest("GET", "/something");
1101 		request.addHeader("Accept", "application/xml");
1102 		response = new MockHttpServletResponse();
1103 		getServlet().service(request, response);
1104 		assertEquals("xml", response.getContentAsString());
1105 
1106 		request = new MockHttpServletRequest("GET", "/something");
1107 		request.addHeader("Accept", "application/xml, text/html");
1108 		response = new MockHttpServletResponse();
1109 		getServlet().service(request, response);
1110 		assertEquals("xml", response.getContentAsString());
1111 
1112 		request = new MockHttpServletRequest("GET", "/something");
1113 		request.addHeader("Accept", "text/html;q=0.9, application/xml");
1114 		response = new MockHttpServletResponse();
1115 		getServlet().service(request, response);
1116 		assertEquals("xml", response.getContentAsString());
1117 
1118 		request = new MockHttpServletRequest("GET", "/something");
1119 		request.addHeader("Accept", "application/msword");
1120 		response = new MockHttpServletResponse();
1121 		getServlet().service(request, response);
1122 		assertEquals(406, response.getStatus());
1123 	}
1124 
1125 	@Test
1126 	public void produces() throws ServletException, IOException {
1127 		initServletWithControllers(ProducesController.class);
1128 
1129 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/something");
1130 		request.addHeader("Accept", "text/html");
1131 		MockHttpServletResponse response = new MockHttpServletResponse();
1132 		getServlet().service(request, response);
1133 		assertEquals("html", response.getContentAsString());
1134 
1135 		request = new MockHttpServletRequest("GET", "/something");
1136 		request.addHeader("Accept", "application/xml");
1137 		response = new MockHttpServletResponse();
1138 		getServlet().service(request, response);
1139 		assertEquals("xml", response.getContentAsString());
1140 
1141 		request = new MockHttpServletRequest("GET", "/something");
1142 		request.addHeader("Accept", "application/xml, text/html");
1143 		response = new MockHttpServletResponse();
1144 		getServlet().service(request, response);
1145 		assertEquals("xml", response.getContentAsString());
1146 
1147 		request = new MockHttpServletRequest("GET", "/something");
1148 		request.addHeader("Accept", "text/html;q=0.9, application/xml");
1149 		response = new MockHttpServletResponse();
1150 		getServlet().service(request, response);
1151 		assertEquals("xml", response.getContentAsString());
1152 
1153 		request = new MockHttpServletRequest("GET", "/something");
1154 		request.addHeader("Accept", "application/msword");
1155 		response = new MockHttpServletResponse();
1156 		getServlet().service(request, response);
1157 		assertEquals(406, response.getStatus());
1158 	}
1159 
1160 	@Test
1161 	public void responseStatus() throws ServletException, IOException {
1162 		initServletWithControllers(ResponseStatusController.class);
1163 
1164 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/something");
1165 		MockHttpServletResponse response = new MockHttpServletResponse();
1166 		getServlet().service(request, response);
1167 		assertEquals("something", response.getContentAsString());
1168 		assertEquals(201, response.getStatus());
1169 		assertEquals("It's alive!", response.getErrorMessage());
1170 	}
1171 
1172 	@Test
1173 	public void mavResolver() throws ServletException, IOException {
1174 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
1175 			@Override
1176 			public void initialize(GenericWebApplicationContext wac) {
1177 				RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
1178 				ModelAndViewResolver[] mavResolvers = new ModelAndViewResolver[] {new MyModelAndViewResolver()};
1179 				adapterDef.getPropertyValues().add("modelAndViewResolvers", mavResolvers);
1180 				wac.registerBeanDefinition("handlerAdapter", adapterDef);
1181 			}
1182 		}, ModelAndViewResolverController.class);
1183 
1184 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
1185 		MockHttpServletResponse response = new MockHttpServletResponse();
1186 		getServlet().service(request, response);
1187 		assertEquals("myValue", response.getContentAsString());
1188 
1189 	}
1190 
1191 	@Test
1192 	public void bindingCookieValue() throws ServletException, IOException {
1193 		initServletWithControllers(BindingCookieValueController.class);
1194 
1195 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test");
1196 		request.setCookies(new Cookie("date", "2008-11-18"));
1197 		MockHttpServletResponse response = new MockHttpServletResponse();
1198 		getServlet().service(request, response);
1199 		assertEquals("test-2008", response.getContentAsString());
1200 	}
1201 
1202 	@Test
1203 	public void ambiguousParams() throws ServletException, IOException {
1204 		initServletWithControllers(AmbiguousParamsController.class);
1205 
1206 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test");
1207 		MockHttpServletResponse response = new MockHttpServletResponse();
1208 		getServlet().service(request, response);
1209 		assertEquals("noParams", response.getContentAsString());
1210 
1211 		request = new MockHttpServletRequest("GET", "/test");
1212 		request.addParameter("myParam", "42");
1213 		response = new MockHttpServletResponse();
1214 		getServlet().service(request, response);
1215 		assertEquals("myParam-42", response.getContentAsString());
1216 	}
1217 
1218 	// SPR-9062
1219 
1220 	@Test
1221 	public void ambiguousPathAndRequestMethod() throws Exception {
1222 		initServletWithControllers(AmbiguousPathAndRequestMethodController.class);
1223 
1224 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bug/EXISTING");
1225 		MockHttpServletResponse response = new MockHttpServletResponse();
1226 		getServlet().service(request, response);
1227 		assertEquals(200, response.getStatus());
1228 		assertEquals("Pattern", response.getContentAsString());
1229 	}
1230 
1231 	@Test
1232 	public void bridgeMethods() throws Exception {
1233 		initServletWithControllers(TestControllerImpl.class);
1234 
1235 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/method");
1236 		MockHttpServletResponse response = new MockHttpServletResponse();
1237 		getServlet().service(request, response);
1238 	}
1239 
1240 	@Test
1241 	public void requestParamMap() throws Exception {
1242 		initServletWithControllers(RequestParamMapController.class);
1243 
1244 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/map");
1245 		request.addParameter("key1", "value1");
1246 		request.addParameter("key2", new String[]{"value21", "value22"});
1247 		MockHttpServletResponse response = new MockHttpServletResponse();
1248 
1249 		getServlet().service(request, response);
1250 		assertEquals("key1=value1,key2=value21", response.getContentAsString());
1251 
1252 		request.setRequestURI("/multiValueMap");
1253 		response = new MockHttpServletResponse();
1254 
1255 		getServlet().service(request, response);
1256 		assertEquals("key1=[value1],key2=[value21,value22]", response.getContentAsString());
1257 	}
1258 
1259 	@Test
1260 	public void requestHeaderMap() throws Exception {
1261 		initServletWithControllers(RequestHeaderMapController.class);
1262 
1263 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/map");
1264 		request.addHeader("Content-Type", "text/html");
1265 		request.addHeader("Custom-Header", new String[]{"value21", "value22"});
1266 		MockHttpServletResponse response = new MockHttpServletResponse();
1267 
1268 		getServlet().service(request, response);
1269 		assertEquals("Content-Type=text/html,Custom-Header=value21", response.getContentAsString());
1270 
1271 		request.setRequestURI("/multiValueMap");
1272 		response = new MockHttpServletResponse();
1273 
1274 		getServlet().service(request, response);
1275 		assertEquals("Content-Type=[text/html],Custom-Header=[value21,value22]", response.getContentAsString());
1276 
1277 		request.setRequestURI("/httpHeaders");
1278 		response = new MockHttpServletResponse();
1279 
1280 		getServlet().service(request, response);
1281 		assertEquals("Content-Type=[text/html],Custom-Header=[value21,value22]", response.getContentAsString());
1282 	}
1283 
1284 
1285 	@Test
1286 	public void requestMappingInterface() throws Exception {
1287 		initServletWithControllers(IMyControllerImpl.class);
1288 
1289 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/handle");
1290 		MockHttpServletResponse response = new MockHttpServletResponse();
1291 		getServlet().service(request, response);
1292 		assertEquals("handle null", response.getContentAsString());
1293 
1294 		request = new MockHttpServletRequest("GET", "/handle");
1295 		request.addParameter("p", "value");
1296 		response = new MockHttpServletResponse();
1297 		getServlet().service(request, response);
1298 		assertEquals("handle value", response.getContentAsString());
1299 	}
1300 
1301 	@Test
1302 	public void requestMappingInterfaceWithProxy() throws Exception {
1303 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
1304 			@Override
1305 			public void initialize(GenericWebApplicationContext wac) {
1306 				DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
1307 				autoProxyCreator.setBeanFactory(wac.getBeanFactory());
1308 				wac.getBeanFactory().addBeanPostProcessor(autoProxyCreator);
1309 				wac.getBeanFactory().registerSingleton("advisor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
1310 			}
1311 		}, IMyControllerImpl.class);
1312 
1313 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/handle");
1314 		MockHttpServletResponse response = new MockHttpServletResponse();
1315 		getServlet().service(request, response);
1316 		assertEquals("handle null", response.getContentAsString());
1317 
1318 		request = new MockHttpServletRequest("GET", "/handle");
1319 		request.addParameter("p", "value");
1320 		response = new MockHttpServletResponse();
1321 		getServlet().service(request, response);
1322 		assertEquals("handle value", response.getContentAsString());
1323 	}
1324 
1325 	@Test
1326 	public void requestMappingBaseClass() throws Exception {
1327 		initServletWithControllers(MyAbstractControllerImpl.class);
1328 
1329 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/handle");
1330 		MockHttpServletResponse response = new MockHttpServletResponse();
1331 		getServlet().service(request, response);
1332 		assertEquals("handle", response.getContentAsString());
1333 
1334 	}
1335 
1336 	@Test
1337 	public void trailingSlash() throws Exception {
1338 		initServletWithControllers(TrailingSlashController.class);
1339 
1340 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo/");
1341 		MockHttpServletResponse response = new MockHttpServletResponse();
1342 		getServlet().service(request, response);
1343 		assertEquals("templatePath", response.getContentAsString());
1344 	}
1345 
1346 	/*
1347 	 * See SPR-6021
1348 	 */
1349 	@Test
1350 	public void customMapEditor() throws Exception {
1351 		initServletWithControllers(CustomMapEditorController.class);
1352 
1353 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/handle");
1354 		request.addParameter("map", "bar");
1355 		MockHttpServletResponse response = new MockHttpServletResponse();
1356 
1357 		getServlet().service(request, response);
1358 
1359 		assertEquals("test-{foo=bar}", response.getContentAsString());
1360 	}
1361 
1362 	@Test
1363 	public void multipartFileAsSingleString() throws Exception {
1364 		initServletWithControllers(MultipartController.class);
1365 
1366 		MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
1367 		request.setRequestURI("/singleString");
1368 		request.addFile(new MockMultipartFile("content", "Juergen".getBytes()));
1369 		MockHttpServletResponse response = new MockHttpServletResponse();
1370 		getServlet().service(request, response);
1371 		assertEquals("Juergen", response.getContentAsString());
1372 	}
1373 
1374 	@Test
1375 	public void regularParameterAsSingleString() throws Exception {
1376 		initServletWithControllers(MultipartController.class);
1377 
1378 		MockHttpServletRequest request = new MockHttpServletRequest();
1379 		request.setRequestURI("/singleString");
1380 		request.setMethod("POST");
1381 		request.addParameter("content", "Juergen");
1382 		MockHttpServletResponse response = new MockHttpServletResponse();
1383 		getServlet().service(request, response);
1384 		assertEquals("Juergen", response.getContentAsString());
1385 	}
1386 
1387 	@Test
1388 	public void multipartFileAsStringArray() throws Exception {
1389 		initServletWithControllers(MultipartController.class);
1390 
1391 		MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
1392 		request.setRequestURI("/stringArray");
1393 		request.addFile(new MockMultipartFile("content", "Juergen".getBytes()));
1394 		MockHttpServletResponse response = new MockHttpServletResponse();
1395 		getServlet().service(request, response);
1396 		assertEquals("Juergen", response.getContentAsString());
1397 	}
1398 
1399 	@Test
1400 	public void regularParameterAsStringArray() throws Exception {
1401 		initServletWithControllers(MultipartController.class);
1402 
1403 		MockHttpServletRequest request = new MockHttpServletRequest();
1404 		request.setRequestURI("/stringArray");
1405 		request.setMethod("POST");
1406 		request.addParameter("content", "Juergen");
1407 		MockHttpServletResponse response = new MockHttpServletResponse();
1408 		getServlet().service(request, response);
1409 		assertEquals("Juergen", response.getContentAsString());
1410 	}
1411 
1412 	@Test
1413 	public void multipartFilesAsStringArray() throws Exception {
1414 		initServletWithControllers(MultipartController.class);
1415 
1416 		MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
1417 		request.setRequestURI("/stringArray");
1418 		request.addFile(new MockMultipartFile("content", "Juergen".getBytes()));
1419 		request.addFile(new MockMultipartFile("content", "Eva".getBytes()));
1420 		MockHttpServletResponse response = new MockHttpServletResponse();
1421 		getServlet().service(request, response);
1422 		assertEquals("Juergen-Eva", response.getContentAsString());
1423 	}
1424 
1425 	@Test
1426 	public void regularParametersAsStringArray() throws Exception {
1427 		initServletWithControllers(MultipartController.class);
1428 
1429 		MockHttpServletRequest request = new MockHttpServletRequest();
1430 		request.setRequestURI("/stringArray");
1431 		request.setMethod("POST");
1432 		request.addParameter("content", "Juergen");
1433 		request.addParameter("content", "Eva");
1434 		MockHttpServletResponse response = new MockHttpServletResponse();
1435 		getServlet().service(request, response);
1436 		assertEquals("Juergen-Eva", response.getContentAsString());
1437 	}
1438 
1439 	@Test
1440 	public void parameterCsvAsStringArray() throws Exception {
1441 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
1442 			@Override
1443 			public void initialize(GenericWebApplicationContext wac) {
1444 				RootBeanDefinition csDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class);
1445 				RootBeanDefinition wbiDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
1446 				wbiDef.getPropertyValues().add("conversionService", csDef);
1447 				RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
1448 				adapterDef.getPropertyValues().add("webBindingInitializer", wbiDef);
1449 				wac.registerBeanDefinition("handlerAdapter", adapterDef);
1450 			}
1451 		}, CsvController.class);
1452 
1453 		MockHttpServletRequest request = new MockHttpServletRequest();
1454 		request.setRequestURI("/integerArray");
1455 		request.setMethod("POST");
1456 		request.addParameter("content", "1,2");
1457 		MockHttpServletResponse response = new MockHttpServletResponse();
1458 		getServlet().service(request, response);
1459 		assertEquals("1-2", response.getContentAsString());
1460 	}
1461 
1462 	@Test
1463 	public void testMatchWithoutMethodLevelPath() throws Exception {
1464 		initServletWithControllers(NoPathGetAndM2PostController.class);
1465 
1466 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/t1/m2");
1467 		MockHttpServletResponse response = new MockHttpServletResponse();
1468 		getServlet().service(request, response);
1469 		assertEquals(405, response.getStatus());
1470 	}
1471 
1472 	// SPR-8536
1473 
1474 	@Test
1475 	public void testHeadersCondition() throws Exception {
1476 		initServletWithControllers(HeadersConditionController.class);
1477 
1478 		// No "Accept" header
1479 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
1480 		MockHttpServletResponse response = new MockHttpServletResponse();
1481 		getServlet().service(request, response);
1482 
1483 		assertEquals(200, response.getStatus());
1484 		assertEquals("home", response.getForwardedUrl());
1485 
1486 		// Accept "*/*"
1487 		request = new MockHttpServletRequest("GET", "/");
1488 		request.addHeader("Accept", "*/*");
1489 		response = new MockHttpServletResponse();
1490 		getServlet().service(request, response);
1491 
1492 		assertEquals(200, response.getStatus());
1493 		assertEquals("home", response.getForwardedUrl());
1494 
1495 		// Accept "application/json"
1496 		request = new MockHttpServletRequest("GET", "/");
1497 		request.addHeader("Accept", "application/json");
1498 		response = new MockHttpServletResponse();
1499 		getServlet().service(request, response);
1500 
1501 		assertEquals(200, response.getStatus());
1502 		assertEquals("application/json", response.getHeader("Content-Type"));
1503 		assertEquals("homeJson", response.getContentAsString());
1504 	}
1505 
1506 	@Test
1507 	public void redirectAttribute() throws Exception {
1508 		initServletWithControllers(RedirectAttributesController.class);
1509 
1510 		MockHttpServletRequest request = new MockHttpServletRequest("POST", "/messages");
1511 		HttpSession session = request.getSession();
1512 		MockHttpServletResponse response = new MockHttpServletResponse();
1513 		getServlet().service(request, response);
1514 
1515 		// POST -> bind error
1516 		getServlet().service(request, response);
1517 
1518 		assertEquals(200, response.getStatus());
1519 		assertEquals("messages/new", response.getForwardedUrl());
1520 		assertTrue(RequestContextUtils.getOutputFlashMap(request).isEmpty());
1521 
1522 		// POST -> success
1523 		request = new MockHttpServletRequest("POST", "/messages");
1524 		request.setSession(session);
1525 		request.addParameter("name", "Jeff");
1526 		response = new MockHttpServletResponse();
1527 		getServlet().service(request, response);
1528 
1529 		assertEquals(302, response.getStatus());
1530 		assertEquals("/messages/1?name=value", response.getRedirectedUrl());
1531 		assertEquals("yay!", RequestContextUtils.getOutputFlashMap(request).get("successMessage"));
1532 
1533 		// GET after POST
1534 		request = new MockHttpServletRequest("GET", "/messages/1");
1535 		request.addParameter("name", "value");
1536 		request.setSession(session);
1537 		response = new MockHttpServletResponse();
1538 		getServlet().service(request, response);
1539 
1540 		assertEquals(200, response.getStatus());
1541 		assertEquals("Got: yay!", response.getContentAsString());
1542 		assertTrue(RequestContextUtils.getOutputFlashMap(request).isEmpty());
1543 	}
1544 
1545 	@Test
1546 	public void prototypeController() throws Exception {
1547 		initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
1548 			@Override
1549 			public void initialize(GenericWebApplicationContext context) {
1550 				RootBeanDefinition beanDef = new RootBeanDefinition(PrototypeController.class);
1551 				beanDef.setScope(BeanDefinition.SCOPE_PROTOTYPE);
1552 				context.registerBeanDefinition("controller", beanDef);
1553 			}
1554 		});
1555 
1556 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
1557 		request.addParameter("param", "1");
1558 		MockHttpServletResponse response = new MockHttpServletResponse();
1559 		getServlet().service(request, response);
1560 
1561 		assertEquals("count:3", response.getContentAsString());
1562 
1563 		response = new MockHttpServletResponse();
1564 		getServlet().service(request, response);
1565 
1566 		assertEquals("count:3", response.getContentAsString());
1567 	}
1568 
1569 	@Test
1570 	public void restController() throws Exception {
1571 		initServletWithControllers(ThisWillActuallyRun.class);
1572 
1573 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
1574 		MockHttpServletResponse response = new MockHttpServletResponse();
1575 		getServlet().service(request, response);
1576 		assertEquals("Hello World!", response.getContentAsString());
1577 	}
1578 
1579 	@Test
1580 	public void responseAsHttpHeaders() throws Exception {
1581 		initServletWithControllers(HttpHeadersResponseController.class);
1582 		MockHttpServletResponse response = new MockHttpServletResponse();
1583 		getServlet().service(new MockHttpServletRequest("POST", "/"), response);
1584 
1585 		assertEquals("Wrong status code", MockHttpServletResponse.SC_CREATED, response.getStatus());
1586 		assertEquals("Wrong number of headers", 1, response.getHeaderNames().size());
1587 		assertEquals("Wrong value for 'location' header", "/test/items/123", response.getHeader("location"));
1588 		assertEquals("Expected an empty content", 0, response.getContentLength());
1589 	}
1590 
1591 	@Test
1592 	public void responseAsHttpHeadersNoHeader() throws Exception {
1593 		initServletWithControllers(HttpHeadersResponseController.class);
1594 		MockHttpServletResponse response = new MockHttpServletResponse();
1595 		getServlet().service(new MockHttpServletRequest("POST", "/empty"), response);
1596 
1597 		assertEquals("Wrong status code", MockHttpServletResponse.SC_CREATED, response.getStatus());
1598 		assertEquals("Wrong number of headers", 0, response.getHeaderNames().size());
1599 		assertEquals("Expected an empty content", 0, response.getContentLength());
1600 	}
1601 
1602 	/*
1603 	 * Controllers
1604 	 */
1605 
1606 	@Controller
1607 	static class ControllerWithEmptyValueMapping {
1608 
1609 		@RequestMapping("")
1610 		public void myPath2(HttpServletResponse response) throws IOException {
1611 			throw new IllegalStateException("test");
1612 		}
1613 
1614 		@RequestMapping("/bar")
1615 		public void myPath3(HttpServletResponse response) throws IOException {
1616 			response.getWriter().write("testX");
1617 		}
1618 
1619 		@ExceptionHandler
1620 		public void myPath2(Exception ex, HttpServletResponse response) throws IOException {
1621 			response.getWriter().write(ex.getMessage());
1622 		}
1623 	}
1624 
1625 	@Controller
1626 	static class MyAdaptedController {
1627 
1628 		@RequestMapping("/myPath1.do")
1629 		public void myHandle(HttpServletRequest request, HttpServletResponse response) throws IOException {
1630 			response.getWriter().write("test");
1631 		}
1632 
1633 		@RequestMapping("/myPath2.do")
1634 		public void myHandle(@RequestParam("param1") String p1, @RequestParam("param2") int p2,
1635 				@RequestHeader("header1") long h1, @CookieValue("cookie1") Cookie c1,
1636 				HttpServletResponse response) throws IOException {
1637 			response.getWriter().write("test-" + p1 + "-" + p2 + "-" + h1 + "-" + c1.getValue());
1638 		}
1639 
1640 		@RequestMapping("/myPath3")
1641 		public void myHandle(TestBean tb, HttpServletResponse response) throws IOException {
1642 			response.getWriter().write("test-" + tb.getName() + "-" + tb.getAge());
1643 		}
1644 
1645 		@RequestMapping("/myPath4.do")
1646 		public void myHandle(TestBean tb, Errors errors, HttpServletResponse response) throws IOException {
1647 			response.getWriter().write("test-" + tb.getName() + "-" + errors.getFieldError("age").getCode());
1648 		}
1649 	}
1650 
1651 	@Controller
1652 	@RequestMapping("/*.do")
1653 	static class MyAdaptedController2 {
1654 
1655 		@RequestMapping
1656 		public void myHandle(HttpServletRequest request, HttpServletResponse response) throws IOException {
1657 			response.getWriter().write("test");
1658 		}
1659 
1660 		@RequestMapping("/myPath2.do")
1661 		public void myHandle(@RequestParam("param1") String p1, int param2, HttpServletResponse response,
1662 				@RequestHeader("header1") String h1, @CookieValue("cookie1") String c1) throws IOException {
1663 			response.getWriter().write("test-" + p1 + "-" + param2 + "-" + h1 + "-" + c1);
1664 		}
1665 
1666 		@RequestMapping("/myPath3")
1667 		public void myHandle(TestBean tb, HttpServletResponse response) throws IOException {
1668 			response.getWriter().write("test-" + tb.getName() + "-" + tb.getAge());
1669 		}
1670 
1671 		@RequestMapping("/myPath4.*")
1672 		public void myHandle(TestBean tb, Errors errors, HttpServletResponse response) throws IOException {
1673 			response.getWriter().write("test-" + tb.getName() + "-" + errors.getFieldError("age").getCode());
1674 		}
1675 	}
1676 
1677 	@Controller
1678 	static class MyAdaptedControllerBase<T> {
1679 
1680 		@RequestMapping("/myPath2.do")
1681 		public void myHandle(@RequestParam("param1") T p1, int param2, @RequestHeader Integer header1,
1682 				@CookieValue int cookie1, HttpServletResponse response) throws IOException {
1683 			response.getWriter().write("test-" + p1 + "-" + param2 + "-" + header1 + "-" + cookie1);
1684 		}
1685 
1686 		@InitBinder
1687 		public void initBinder(@RequestParam("param1") String p1, @RequestParam(value="paramX", required=false) String px, int param2) {
1688 			assertNull(px);
1689 		}
1690 
1691 		@ModelAttribute
1692 		public void modelAttribute(@RequestParam("param1") String p1, @RequestParam(value="paramX", required=false) String px, int param2) {
1693 			assertNull(px);
1694 		}
1695 	}
1696 
1697 	@RequestMapping("/*.do")
1698 	static class MyAdaptedController3 extends MyAdaptedControllerBase<String> {
1699 
1700 		@RequestMapping
1701 		public void myHandle(HttpServletRequest request, HttpServletResponse response) throws IOException {
1702 			response.getWriter().write("test");
1703 		}
1704 
1705 		@Override
1706 		public void myHandle(@RequestParam("param1") String p1, int param2, @RequestHeader Integer header1,
1707 				@CookieValue int cookie1, HttpServletResponse response) throws IOException {
1708 			response.getWriter().write("test-" + p1 + "-" + param2 + "-" + header1 + "-" + cookie1);
1709 		}
1710 
1711 		@RequestMapping("/myPath3")
1712 		public void myHandle(TestBean tb, HttpServletResponse response) throws IOException {
1713 			response.getWriter().write("test-" + tb.getName() + "-" + tb.getAge());
1714 		}
1715 
1716 		@RequestMapping("/myPath4.*")
1717 		public void myHandle(TestBean tb, Errors errors, HttpServletResponse response) throws IOException {
1718 			response.getWriter().write("test-" + tb.getName() + "-" + errors.getFieldError("age").getCode());
1719 		}
1720 
1721 		@Override
1722 		@InitBinder
1723 		public void initBinder(@RequestParam("param1") String p1, @RequestParam(value="paramX", required=false) String px, int param2) {
1724 			assertNull(px);
1725 		}
1726 
1727 		@Override
1728 		@ModelAttribute
1729 		public void modelAttribute(@RequestParam("param1") String p1, @RequestParam(value="paramX", required=false) String px, int param2) {
1730 			assertNull(px);
1731 		}
1732 	}
1733 
1734 	@Controller
1735 	@RequestMapping(method = RequestMethod.GET)
1736 	static class EmptyParameterListHandlerMethodController {
1737 
1738 		static boolean called;
1739 
1740 		@RequestMapping("/emptyParameterListHandler")
1741 		public void emptyParameterListHandler() {
1742 			EmptyParameterListHandlerMethodController.called = true;
1743 		}
1744 
1745 		@RequestMapping("/nonEmptyParameterListHandler")
1746 		public void nonEmptyParameterListHandler(HttpServletResponse response) {
1747 		}
1748 	}
1749 
1750 	@Controller
1751 	@RequestMapping("/myPage")
1752 	@SessionAttributes({"object1", "object2"})
1753 	public static class MySessionAttributesController {
1754 
1755 		@RequestMapping(method = RequestMethod.GET)
1756 		public String get(Model model) {
1757 			model.addAttribute("object1", new Object());
1758 			model.addAttribute("object2", new Object());
1759 			return "page1";
1760 		}
1761 
1762 		@RequestMapping(method = RequestMethod.POST)
1763 		public String post(@ModelAttribute("object1") Object object1) {
1764 			//do something with object1
1765 			return "page2";
1766 
1767 		}
1768 	}
1769 
1770 	@RequestMapping("/myPage")
1771 	@SessionAttributes({"object1", "object2"})
1772 	@Controller
1773 	public interface MySessionAttributesControllerIfc {
1774 
1775 		@RequestMapping(method = RequestMethod.GET)
1776 		String get(Model model);
1777 
1778 		@RequestMapping(method = RequestMethod.POST)
1779 		String post(@ModelAttribute("object1") Object object1);
1780 	}
1781 
1782 	public static class MySessionAttributesControllerImpl implements MySessionAttributesControllerIfc {
1783 
1784 		@Override
1785 		public String get(Model model) {
1786 			model.addAttribute("object1", new Object());
1787 			model.addAttribute("object2", new Object());
1788 			return "page1";
1789 		}
1790 
1791 		@Override
1792 		public String post(@ModelAttribute("object1") Object object1) {
1793 			//do something with object1
1794 			return "page2";
1795 		}
1796 	}
1797 
1798 	@RequestMapping("/myPage")
1799 	@SessionAttributes({"object1", "object2"})
1800 	public interface MyParameterizedControllerIfc<T> {
1801 
1802 		@ModelAttribute("testBeanList")
1803 		List<TestBean> getTestBeans();
1804 
1805 		@RequestMapping(method = RequestMethod.GET)
1806 		String get(Model model);
1807 	}
1808 
1809 	public interface MyEditableParameterizedControllerIfc<T> extends MyParameterizedControllerIfc<T> {
1810 
1811 		@RequestMapping(method = RequestMethod.POST)
1812 		String post(@ModelAttribute("object1") T object);
1813 	}
1814 
1815 	@Controller
1816 	public static class MyParameterizedControllerImpl implements MyEditableParameterizedControllerIfc<TestBean> {
1817 
1818 		@Override
1819 		public List<TestBean> getTestBeans() {
1820 			List<TestBean> list = new LinkedList<TestBean>();
1821 			list.add(new TestBean("tb1"));
1822 			list.add(new TestBean("tb2"));
1823 			return list;
1824 		}
1825 
1826 		@Override
1827 		public String get(Model model) {
1828 			model.addAttribute("object1", new TestBean());
1829 			model.addAttribute("object2", new TestBean());
1830 			return "page1";
1831 		}
1832 
1833 		@Override
1834 		public String post(TestBean object) {
1835 			//do something with object1
1836 			return "page2";
1837 		}
1838 	}
1839 
1840 	@Controller
1841 	public static class MyParameterizedControllerImplWithOverriddenMappings implements MyEditableParameterizedControllerIfc<TestBean> {
1842 
1843 		@Override
1844 		@ModelAttribute("testBeanList")
1845 		public List<TestBean> getTestBeans() {
1846 			List<TestBean> list = new LinkedList<TestBean>();
1847 			list.add(new TestBean("tb1"));
1848 			list.add(new TestBean("tb2"));
1849 			return list;
1850 		}
1851 
1852 		@Override
1853 		@RequestMapping(method = RequestMethod.GET)
1854 		public String get(Model model) {
1855 			model.addAttribute("object1", new TestBean());
1856 			model.addAttribute("object2", new TestBean());
1857 			return "page1";
1858 		}
1859 
1860 		@Override
1861 		@RequestMapping(method = RequestMethod.POST)
1862 		public String post(@ModelAttribute("object1") TestBean object1) {
1863 			//do something with object1
1864 			return "page2";
1865 		}
1866 	}
1867 
1868 	@Controller
1869 	public static class MyFormController {
1870 
1871 		@ModelAttribute("testBeanList")
1872 		public List<TestBean> getTestBeans() {
1873 			List<TestBean> list = new LinkedList<TestBean>();
1874 			list.add(new TestBean("tb1"));
1875 			list.add(new TestBean("tb2"));
1876 			return list;
1877 		}
1878 
1879 		@RequestMapping("/myPath.do")
1880 		public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, ModelMap model) {
1881 			FieldError error = errors.getFieldError("age");
1882 			assertNotNull("Must have field error for age property", error);
1883 			assertEquals("value2", error.getRejectedValue());
1884 			if (!model.containsKey("myKey")) {
1885 				model.addAttribute("myKey", "myValue");
1886 			}
1887 			return "myView";
1888 		}
1889 	}
1890 
1891 	public static class ValidTestBean extends TestBean {
1892 
1893 		@NotNull
1894 		private String validCountry;
1895 
1896 		public void setValidCountry(String validCountry) {
1897 			this.validCountry = validCountry;
1898 		}
1899 
1900 		public String getValidCountry() {
1901 			return this.validCountry;
1902 		}
1903 	}
1904 
1905 	@Controller
1906 	public static class MyModelFormController {
1907 
1908 		@ModelAttribute
1909 		public List<TestBean> getTestBeans() {
1910 			List<TestBean> list = new LinkedList<TestBean>();
1911 			list.add(new TestBean("tb1"));
1912 			list.add(new TestBean("tb2"));
1913 			return list;
1914 		}
1915 
1916 		@RequestMapping("/myPath.do")
1917 		@ModelAttribute("yourKey")
1918 		public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, Model model) {
1919 			if (!model.containsAttribute("myKey")) {
1920 				model.addAttribute("myKey", "myValue");
1921 			}
1922 			return "yourValue";
1923 		}
1924 	}
1925 
1926 	@Controller
1927 	static class MyCommandProvidingFormController<T, TB, TB2> extends MyFormController {
1928 
1929 		@SuppressWarnings("unused")
1930 		@ModelAttribute("myCommand")
1931 		private ValidTestBean createTestBean(@RequestParam T defaultName,
1932 				Map<String, Object> model,
1933 				@RequestParam Date date) {
1934 			model.put("myKey", "myOriginalValue");
1935 			ValidTestBean tb = new ValidTestBean();
1936 			tb.setName(defaultName.getClass().getSimpleName() + ":" + defaultName.toString());
1937 			return tb;
1938 		}
1939 
1940 		@Override
1941 		@RequestMapping("/myPath.do")
1942 		public String myHandle(@ModelAttribute("myCommand") @Valid TestBean tb, BindingResult errors, ModelMap model) {
1943 			if (!errors.hasFieldErrors("validCountry")) {
1944 				throw new IllegalStateException("Declarative validation not applied");
1945 			}
1946 			return super.myHandle(tb, errors, model);
1947 		}
1948 
1949 		@RequestMapping("/myOtherPath.do")
1950 		public String myOtherHandle(TB tb, BindingResult errors, ExtendedModelMap model, MySpecialArg arg) {
1951 			TestBean tbReal = (TestBean) tb;
1952 			tbReal.setName("myName");
1953 			assertTrue(model.get("ITestBean") instanceof DerivedTestBean);
1954 			assertNotNull(arg);
1955 			return super.myHandle(tbReal, errors, model);
1956 		}
1957 
1958 		@RequestMapping("/myThirdPath.do")
1959 		public String myThirdHandle(TB tb, Model model) {
1960 			model.addAttribute("testBean", new TestBean("special", 99));
1961 			return "myView";
1962 		}
1963 
1964 		@SuppressWarnings("unchecked")
1965 		@ModelAttribute
1966 		protected TB2 getModelAttr() {
1967 			return (TB2) new DerivedTestBean();
1968 		}
1969 	}
1970 
1971 	static class MySpecialArg {
1972 
1973 		public MySpecialArg(String value) {
1974 		}
1975 	}
1976 
1977 	@Controller
1978 	static class MyTypedCommandProvidingFormController
1979 			extends MyCommandProvidingFormController<Integer, TestBean, ITestBean> {
1980 
1981 	}
1982 
1983 	@Controller
1984 	static class MyBinderInitializingCommandProvidingFormController
1985 			extends MyCommandProvidingFormController<String, TestBean, ITestBean> {
1986 
1987 		@SuppressWarnings("unused")
1988 		@InitBinder
1989 		private void initBinder(WebDataBinder binder) {
1990 			binder.initBeanPropertyAccess();
1991 			binder.setRequiredFields("sex");
1992 			LocalValidatorFactoryBean vf = new LocalValidatorFactoryBean();
1993 			vf.afterPropertiesSet();
1994 			binder.setValidator(vf);
1995 			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
1996 			dateFormat.setLenient(false);
1997 			binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
1998 		}
1999 
2000 		@Override
2001 		@RequestMapping("/myPath.do")
2002 		public String myHandle(@ModelAttribute("myCommand") @Valid TestBean tb, BindingResult errors, ModelMap model) {
2003 			if (!errors.hasFieldErrors("sex")) {
2004 				throw new IllegalStateException("requiredFields not applied");
2005 			}
2006 			return super.myHandle(tb, errors, model);
2007 		}
2008 	}
2009 
2010 	@Controller
2011 	static class MySpecificBinderInitializingCommandProvidingFormController
2012 			extends MyCommandProvidingFormController<String, TestBean, ITestBean> {
2013 
2014 		@SuppressWarnings("unused")
2015 		@InitBinder({"myCommand", "date"})
2016 		private void initBinder(WebDataBinder binder, String date, @RequestParam("date") String[] date2) {
2017 			LocalValidatorFactoryBean vf = new LocalValidatorFactoryBean();
2018 			vf.afterPropertiesSet();
2019 			binder.setValidator(vf);
2020 			assertEquals("2007-10-02", date);
2021 			assertEquals(1, date2.length);
2022 			assertEquals("2007-10-02", date2[0]);
2023 			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
2024 			dateFormat.setLenient(false);
2025 			binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
2026 		}
2027 	}
2028 
2029 	static class MyWebBindingInitializer implements WebBindingInitializer {
2030 
2031 		@Override
2032 		public void initBinder(WebDataBinder binder, WebRequest request) {
2033 			LocalValidatorFactoryBean vf = new LocalValidatorFactoryBean();
2034 			vf.afterPropertiesSet();
2035 			binder.setValidator(vf);
2036 			assertNotNull(request.getLocale());
2037 			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
2038 			dateFormat.setLenient(false);
2039 			binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
2040 		}
2041 	}
2042 
2043 	static class MySpecialArgumentResolver implements WebArgumentResolver {
2044 
2045 		@Override
2046 		public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) {
2047 			if (methodParameter.getParameterType().equals(MySpecialArg.class)) {
2048 				return new MySpecialArg("myValue");
2049 			}
2050 			return UNRESOLVED;
2051 		}
2052 	}
2053 
2054 	@Controller
2055 	@RequestMapping("/myPath.do")
2056 	static class MyParameterDispatchingController implements Serializable {
2057 
2058 		private static final long serialVersionUID = 1L;
2059 
2060 		@Autowired
2061 		private transient ServletContext servletContext;
2062 
2063 		@Autowired
2064 		private transient ServletConfig servletConfig;
2065 
2066 		@Autowired
2067 		private HttpSession session;
2068 
2069 		@Autowired
2070 		private HttpServletRequest request;
2071 
2072 		@Autowired
2073 		private WebRequest webRequest;
2074 
2075 		@RequestMapping
2076 		public void myHandle(HttpServletResponse response, HttpServletRequest request) throws IOException {
2077 			if (this.servletContext == null || this.servletConfig == null || this.session == null ||
2078 					this.request == null || this.webRequest == null) {
2079 				throw new IllegalStateException();
2080 			}
2081 			response.getWriter().write("myView");
2082 			request.setAttribute("servletContext", this.servletContext);
2083 			request.setAttribute("servletConfig", this.servletConfig);
2084 			request.setAttribute("sessionId", this.session.getId());
2085 			request.setAttribute("requestUri", this.request.getRequestURI());
2086 			request.setAttribute("locale", this.webRequest.getLocale());
2087 		}
2088 
2089 		@RequestMapping(params = {"view", "!lang"})
2090 		public void myOtherHandle(HttpServletResponse response) throws IOException {
2091 			response.getWriter().write("myOtherView");
2092 		}
2093 
2094 		@RequestMapping(method = RequestMethod.GET, params = {"view=my", "lang=de"})
2095 		public void myLangHandle(HttpServletResponse response) throws IOException {
2096 			response.getWriter().write("myLangView");
2097 		}
2098 
2099 		@RequestMapping(method = {RequestMethod.POST, RequestMethod.GET}, params = "surprise")
2100 		public void mySurpriseHandle(HttpServletResponse response) throws IOException {
2101 			response.getWriter().write("mySurpriseView");
2102 		}
2103 	}
2104 
2105 	@Controller
2106 	@RequestMapping(value = "/myPath.do", params = {"active"})
2107 	static class MyConstrainedParameterDispatchingController {
2108 
2109 		@RequestMapping(params = {"view", "!lang"})
2110 		public void myOtherHandle(HttpServletResponse response) throws IOException {
2111 			response.getWriter().write("myOtherView");
2112 		}
2113 
2114 		@RequestMapping(method = RequestMethod.GET, params = {"view=my", "lang=de"})
2115 		public void myLangHandle(HttpServletResponse response) throws IOException {
2116 			response.getWriter().write("myLangView");
2117 		}
2118 	}
2119 
2120 	@Controller
2121 	@RequestMapping("/myApp/*")
2122 	static class MyRelativePathDispatchingController {
2123 
2124 		@RequestMapping
2125 		public void myHandle(HttpServletResponse response) throws IOException {
2126 			response.getWriter().write("myView");
2127 		}
2128 
2129 		@RequestMapping("*Other")
2130 		public void myOtherHandle(HttpServletResponse response) throws IOException {
2131 			response.getWriter().write("myOtherView");
2132 		}
2133 
2134 		@RequestMapping("myLang")
2135 		public void myLangHandle(HttpServletResponse response) throws IOException {
2136 			response.getWriter().write("myLangView");
2137 		}
2138 
2139 		@RequestMapping("surprise")
2140 		public void mySurpriseHandle(HttpServletResponse response) throws IOException {
2141 			response.getWriter().write("mySurpriseView");
2142 		}
2143 	}
2144 
2145 	@Controller
2146 	static class MyRelativeMethodPathDispatchingController {
2147 
2148 		@RequestMapping("**/myHandle")
2149 		public void myHandle(HttpServletResponse response) throws IOException {
2150 			response.getWriter().write("myView");
2151 		}
2152 
2153 		@RequestMapping("/**/*Other")
2154 		public void myOtherHandle(HttpServletResponse response) throws IOException {
2155 			response.getWriter().write("myOtherView");
2156 		}
2157 
2158 		@RequestMapping("**/myLang")
2159 		public void myLangHandle(HttpServletResponse response) throws IOException {
2160 			response.getWriter().write("myLangView");
2161 		}
2162 
2163 		@RequestMapping("/**/surprise")
2164 		public void mySurpriseHandle(HttpServletResponse response) throws IOException {
2165 			response.getWriter().write("mySurpriseView");
2166 		}
2167 	}
2168 
2169 	@Controller
2170 	static class MyNullCommandController {
2171 
2172 		@ModelAttribute
2173 		public TestBean getTestBean() {
2174 			return null;
2175 		}
2176 
2177 		@ModelAttribute
2178 		public Principal getPrincipal() {
2179 			return new TestPrincipal();
2180 		}
2181 
2182 		@RequestMapping("/myPath")
2183 		public void handle(@ModelAttribute TestBean testBean,
2184 				Errors errors,
2185 				@ModelAttribute TestPrincipal modelPrinc,
2186 				OtherPrincipal requestPrinc,
2187 				Writer writer) throws IOException {
2188 			assertNull(testBean);
2189 			assertNotNull(modelPrinc);
2190 			assertNotNull(requestPrinc);
2191 			assertFalse(errors.hasErrors());
2192 			errors.reject("myCode");
2193 			writer.write("myView");
2194 		}
2195 	}
2196 
2197 	static class TestPrincipal implements Principal {
2198 
2199 		@Override
2200 		public String getName() {
2201 			return "test";
2202 		}
2203 	}
2204 
2205 	static class OtherPrincipal implements Principal {
2206 
2207 		@Override
2208 		public String getName() {
2209 			return "other";
2210 		}
2211 	}
2212 
2213 	static class TestViewResolver implements ViewResolver {
2214 
2215 		@Override
2216 		public View resolveViewName(final String viewName, Locale locale) throws Exception {
2217 			return new View() {
2218 				@Override
2219 				public String getContentType() {
2220 					return null;
2221 				}
2222 
2223 				@Override
2224 				@SuppressWarnings({"unchecked", "deprecation", "rawtypes"})
2225 				public void render(Map model, HttpServletRequest request, HttpServletResponse response)
2226 						throws Exception {
2227 					TestBean tb = (TestBean) model.get("testBean");
2228 					if (tb == null) {
2229 						tb = (TestBean) model.get("myCommand");
2230 					}
2231 					if (tb.getName() != null && tb.getName().endsWith("myDefaultName")) {
2232 						assertEquals(107, tb.getDate().getYear());
2233 					}
2234 					Errors errors = (Errors) model.get(BindingResult.MODEL_KEY_PREFIX + "testBean");
2235 					if (errors == null) {
2236 						errors = (Errors) model.get(BindingResult.MODEL_KEY_PREFIX + "myCommand");
2237 					}
2238 					if (errors.hasFieldErrors("date")) {
2239 						throw new IllegalStateException();
2240 					}
2241 					if (model.containsKey("ITestBean")) {
2242 						assertTrue(model.get(BindingResult.MODEL_KEY_PREFIX + "ITestBean") instanceof Errors);
2243 					}
2244 					List<TestBean> testBeans = (List<TestBean>) model.get("testBeanList");
2245 					if (errors.hasFieldErrors("age")) {
2246 						response.getWriter()
2247 								.write(viewName + "-" + tb.getName() + "-" + errors.getFieldError("age").getCode() +
2248 										"-" + testBeans.get(0).getName() + "-" + model.get("myKey") +
2249 										(model.containsKey("yourKey") ? "-" + model.get("yourKey") : ""));
2250 					}
2251 					else {
2252 						response.getWriter().write(viewName + "-" + tb.getName() + "-" + tb.getAge() + "-" +
2253 								errors.getFieldValue("name") + "-" + errors.getFieldValue("age"));
2254 					}
2255 				}
2256 			};
2257 		}
2258 	}
2259 
2260 	public static class ModelExposingViewResolver implements ViewResolver {
2261 
2262 		@Override
2263 		public View resolveViewName(final String viewName, Locale locale) throws Exception {
2264 			return new View() {
2265 				@Override
2266 				public String getContentType() {
2267 					return null;
2268 				}
2269 				@Override
2270 				public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) {
2271 					request.setAttribute("viewName", viewName);
2272 					request.getSession().setAttribute("model", model);
2273 				}
2274 			};
2275 		}
2276 	}
2277 
2278 	public static class ParentController {
2279 
2280 		@RequestMapping(method = RequestMethod.GET)
2281 		public void doGet(HttpServletRequest req, HttpServletResponse resp) {
2282 		}
2283 	}
2284 
2285 	@Controller
2286 	@RequestMapping("/child/test")
2287 	public static class ChildController extends ParentController {
2288 
2289 		@RequestMapping(method = RequestMethod.GET)
2290 		public void doGet(HttpServletRequest req, HttpServletResponse resp, @RequestParam("childId") String id) {
2291 		}
2292 	}
2293 
2294 	@Target({ElementType.TYPE})
2295 	@Retention(RetentionPolicy.RUNTIME)
2296 	@Controller
2297 	public @interface MyControllerAnnotation {
2298 
2299 	}
2300 
2301 	@MyControllerAnnotation
2302 	public static class CustomAnnotationController {
2303 
2304 		@RequestMapping("/myPath.do")
2305 		public void myHandle() {
2306 		}
2307 	}
2308 
2309 	@Controller
2310 	public static class RequiredParamController {
2311 
2312 		@RequestMapping("/myPath.do")
2313 		public void myHandle(@RequestParam(value = "id", required = true) int id,
2314 				@RequestHeader(value = "header", required = true) String header) {
2315 		}
2316 	}
2317 
2318 	@Controller
2319 	public static class OptionalParamController {
2320 
2321 		@RequestMapping("/myPath.do")
2322 		public void myHandle(@RequestParam(required = false) String id,
2323 				@RequestParam(required = false) boolean flag,
2324 				@RequestHeader(value = "header", required = false) String header,
2325 				HttpServletResponse response) throws IOException {
2326 			response.getWriter().write(String.valueOf(id) + "-" + flag + "-" + String.valueOf(header));
2327 		}
2328 	}
2329 
2330 	@Controller
2331 	public static class DefaultValueParamController {
2332 
2333 		@RequestMapping("/myPath.do")
2334 		public void myHandle(@RequestParam(value = "id", defaultValue = "foo") String id,
2335 				@RequestParam(value = "otherId", defaultValue = "") String id2,
2336 				@RequestHeader(defaultValue = "bar") String header,
2337 				HttpServletResponse response) throws IOException {
2338 			response.getWriter().write(String.valueOf(id) + "-" + String.valueOf(id2) + "-" + String.valueOf(header));
2339 		}
2340 	}
2341 
2342 	@Controller
2343 	public static class DefaultExpressionValueParamController {
2344 
2345 		@RequestMapping("/myPath.do")
2346 		public void myHandle(@RequestParam(value = "id", defaultValue = "${myKey}") String id,
2347 				@RequestHeader(defaultValue = "#{systemProperties.myHeader}") String header,
2348 				@Value("#{request.contextPath}") String contextPath,
2349 				HttpServletResponse response) throws IOException {
2350 			response.getWriter().write(String.valueOf(id) + "-" + String.valueOf(header) + "-" + contextPath);
2351 		}
2352 	}
2353 
2354 	@Controller
2355 	public static class NestedSetController {
2356 
2357 		@RequestMapping("/myPath.do")
2358 		public void myHandle(GenericBean<?> gb, HttpServletResponse response) throws Exception {
2359 			response.getWriter().write(gb.getTestBeanSet().toString() + "-" +
2360 					gb.getTestBeanSet().iterator().next().getClass().getName());
2361 		}
2362 	}
2363 
2364 	public static class TestBeanConverter implements Converter<String, ITestBean> {
2365 
2366 		@Override
2367 		public ITestBean convert(String source) {
2368 			return new TestBean(source);
2369 		}
2370 	}
2371 
2372 	@Controller
2373 	public static class MethodNotAllowedController {
2374 
2375 		@RequestMapping(value = "/myPath.do", method = RequestMethod.DELETE)
2376 		public void delete() {
2377 		}
2378 
2379 		@RequestMapping(value = "/myPath.do", method = RequestMethod.HEAD)
2380 		public void head() {
2381 		}
2382 
2383 		@RequestMapping(value = "/myPath.do", method = RequestMethod.OPTIONS)
2384 		public void options() {
2385 		}
2386 
2387 		@RequestMapping(value = "/myPath.do", method = RequestMethod.POST)
2388 		public void post() {
2389 		}
2390 
2391 		@RequestMapping(value = "/myPath.do", method = RequestMethod.PUT)
2392 		public void put() {
2393 		}
2394 
2395 		@RequestMapping(value = "/myPath.do", method = RequestMethod.TRACE)
2396 		public void trace() {
2397 		}
2398 
2399 		@RequestMapping(value = "/otherPath.do", method = RequestMethod.GET)
2400 		public void get() {
2401 		}
2402 	}
2403 
2404 	@Controller
2405 	public static class PathOrderingController {
2406 
2407 		@RequestMapping(value = {"/dir/myPath1.do", "/**/*.do"})
2408 		public void method1(Writer writer) throws IOException {
2409 			writer.write("method1");
2410 		}
2411 
2412 		@RequestMapping("/dir/*.do")
2413 		public void method2(Writer writer) throws IOException {
2414 			writer.write("method2");
2415 		}
2416 	}
2417 
2418 	@Controller
2419 	public static class RequestResponseBodyController {
2420 
2421 		@RequestMapping(value = "/something", method = RequestMethod.PUT)
2422 		@ResponseBody
2423 		public String handle(@RequestBody String body) throws IOException {
2424 			return body;
2425 		}
2426 
2427 		@RequestMapping(value = "/something", method = RequestMethod.PATCH)
2428 		@ResponseBody
2429 		public String handlePartialUpdate(@RequestBody String content) throws IOException {
2430 			return content;
2431 		}
2432 	}
2433 
2434 	@Controller
2435 	public static class RequestResponseBodyProducesController {
2436 
2437 		@RequestMapping(value = "/something", method = RequestMethod.PUT, produces = "text/plain")
2438 		@ResponseBody
2439 		public String handle(@RequestBody String body) throws IOException {
2440 			return body;
2441 		}
2442 	}
2443 
2444 	@Controller
2445 	public static class ResponseBodyVoidController {
2446 
2447 		@RequestMapping("/something")
2448 		@ResponseBody
2449 		public void handle() throws IOException {
2450 		}
2451 	}
2452 
2453 	@Controller
2454 	public static class RequestBodyArgMismatchController {
2455 
2456 		@RequestMapping(value = "/something", method = RequestMethod.PUT)
2457 		public void handle(@RequestBody A a) throws IOException {
2458 		}
2459 	}
2460 
2461 	@XmlRootElement
2462 	public static class A {
2463 
2464 	}
2465 
2466 	@XmlRootElement
2467 	public static class B {
2468 
2469 	}
2470 
2471 
2472 	public static class NotReadableMessageConverter implements HttpMessageConverter<Object> {
2473 
2474 		@Override
2475 		public boolean canRead(Class<?> clazz, MediaType mediaType) {
2476 			return true;
2477 		}
2478 
2479 		@Override
2480 		public boolean canWrite(Class<?> clazz, MediaType mediaType) {
2481 			return true;
2482 		}
2483 
2484 		@Override
2485 		public List<MediaType> getSupportedMediaTypes() {
2486 			return Collections.singletonList(new MediaType("application", "pdf"));
2487 		}
2488 
2489 		@Override
2490 		public Object read(Class<?> clazz, HttpInputMessage inputMessage)
2491 				throws IOException, HttpMessageNotReadableException {
2492 			throw new HttpMessageNotReadableException("Could not read");
2493 		}
2494 
2495 		@Override
2496 		public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage)
2497 				throws IOException, HttpMessageNotWritableException {
2498 			throw new UnsupportedOperationException("Not implemented");
2499 		}
2500 	}
2501 
2502 	public static class SimpleMessageConverter implements HttpMessageConverter<Object> {
2503 
2504 		private final List<MediaType> supportedMediaTypes;
2505 
2506 		public SimpleMessageConverter(MediaType... supportedMediaTypes) {
2507 			this.supportedMediaTypes = Arrays.asList(supportedMediaTypes);
2508 		}
2509 
2510 		@Override
2511 		public boolean canRead(Class<?> clazz, MediaType mediaType) {
2512 			return supportedMediaTypes.contains(mediaType);
2513 		}
2514 
2515 		@Override
2516 		public boolean canWrite(Class<?> clazz, MediaType mediaType) {
2517 			return supportedMediaTypes.contains(mediaType);
2518 		}
2519 
2520 		@Override
2521 		public List<MediaType> getSupportedMediaTypes() {
2522 			return supportedMediaTypes;
2523 		}
2524 
2525 		@Override
2526 		public Object read(Class<?> clazz, HttpInputMessage inputMessage)
2527 				throws IOException, HttpMessageNotReadableException {
2528 			return null;
2529 		}
2530 
2531 		@Override
2532 		public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage)
2533 				throws IOException, HttpMessageNotWritableException {
2534 			outputMessage.getHeaders().setContentType(contentType);
2535 			outputMessage.getBody(); // force a header write
2536 		}
2537 	}
2538 
2539 	@Controller
2540 	public static class ContentTypeHeadersController {
2541 
2542 		@RequestMapping(value = "/something", headers = "content-type=application/pdf")
2543 		public void handlePdf(Writer writer) throws IOException {
2544 			writer.write("pdf");
2545 		}
2546 
2547 		@RequestMapping(value = "/something", headers = "content-type=text/*")
2548 		public void handleHtml(Writer writer) throws IOException {
2549 			writer.write("text");
2550 		}
2551 	}
2552 
2553 	@Controller
2554 	public static class ConsumesController {
2555 
2556 		@RequestMapping(value = "/something", consumes = "application/pdf")
2557 		public void handlePdf(Writer writer) throws IOException {
2558 			writer.write("pdf");
2559 		}
2560 
2561 		@RequestMapping(value = "/something", consumes = "text/*")
2562 		public void handleHtml(Writer writer) throws IOException {
2563 			writer.write("text");
2564 		}
2565 	}
2566 
2567 	@Controller
2568 	public static class NegatedContentTypeHeadersController {
2569 
2570 		@RequestMapping(value = "/something", headers = "content-type=application/pdf")
2571 		public void handlePdf(Writer writer) throws IOException {
2572 			writer.write("pdf");
2573 		}
2574 
2575 		@RequestMapping(value = "/something", headers = "content-type!=application/pdf")
2576 		public void handleNonPdf(Writer writer) throws IOException {
2577 			writer.write("non-pdf");
2578 		}
2579 
2580 	}
2581 
2582 	@Controller
2583 	public static class AcceptHeadersController {
2584 
2585 		@RequestMapping(value = "/something", headers = "accept=text/html")
2586 		public void handleHtml(Writer writer) throws IOException {
2587 			writer.write("html");
2588 		}
2589 
2590 		@RequestMapping(value = "/something", headers = "accept=application/xml")
2591 		public void handleXml(Writer writer) throws IOException {
2592 			writer.write("xml");
2593 		}
2594 	}
2595 
2596 	@Controller
2597 	public static class ProducesController {
2598 
2599 		@RequestMapping(value = "/something", produces = "text/html")
2600 		public void handleHtml(Writer writer) throws IOException {
2601 			writer.write("html");
2602 		}
2603 
2604 		@RequestMapping(value = "/something", produces = "application/xml")
2605 		public void handleXml(Writer writer) throws IOException {
2606 			writer.write("xml");
2607 		}
2608 	}
2609 
2610 	@Controller
2611 	public static class ResponseStatusController {
2612 
2613 		@RequestMapping("/something")
2614 		@ResponseStatus(value = HttpStatus.CREATED, reason = "It's alive!")
2615 		public void handle(Writer writer) throws IOException {
2616 			writer.write("something");
2617 		}
2618 	}
2619 
2620 	@Controller
2621 	public static class ModelAndViewResolverController {
2622 
2623 		@RequestMapping("/")
2624 		public MySpecialArg handle() {
2625 			return new MySpecialArg("foo");
2626 		}
2627 	}
2628 
2629 	public static class MyModelAndViewResolver implements ModelAndViewResolver {
2630 
2631 		@Override
2632 		@SuppressWarnings("rawtypes")
2633 		public ModelAndView resolveModelAndView(Method handlerMethod,
2634 				Class handlerType,
2635 				Object returnValue,
2636 				ExtendedModelMap implicitModel,
2637 				NativeWebRequest webRequest) {
2638 			if (returnValue instanceof MySpecialArg) {
2639 				return new ModelAndView(new View() {
2640 					@Override
2641 					public String getContentType() {
2642 						return "text/html";
2643 					}
2644 
2645 					@Override
2646 					public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
2647 							throws Exception {
2648 						response.getWriter().write("myValue");
2649 					}
2650 
2651 				});
2652 			}
2653 			return UNRESOLVED;
2654 		}
2655 	}
2656 
2657 	@Controller
2658 	@RequestMapping("/test*")
2659 	static class AmbiguousParamsController {
2660 
2661 		@RequestMapping(method = RequestMethod.GET)
2662 		public void noParams(Writer writer) throws IOException {
2663 			writer.write("noParams");
2664 		}
2665 
2666 		@RequestMapping(params = "myParam")
2667 		public void param(@RequestParam("myParam") int myParam, Writer writer) throws IOException {
2668 			writer.write("myParam-" + myParam);
2669 		}
2670 	}
2671 
2672 	@Controller
2673 	static class AmbiguousPathAndRequestMethodController {
2674 
2675 		@RequestMapping(value = "/bug/EXISTING", method = RequestMethod.POST)
2676 		public void directMatch(Writer writer) throws IOException {
2677 			writer.write("Direct");
2678 		}
2679 
2680 		@RequestMapping(value = "/bug/{type}", method = RequestMethod.GET)
2681 		public void patternMatch(Writer writer) throws IOException {
2682 			writer.write("Pattern");
2683 		}
2684 	}
2685 
2686 	@Controller
2687 	@RequestMapping("/test*")
2688 	public static class BindingCookieValueController {
2689 
2690 		@InitBinder
2691 		public void initBinder(WebDataBinder binder) {
2692 			binder.initBeanPropertyAccess();
2693 			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
2694 			dateFormat.setLenient(false);
2695 			binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
2696 		}
2697 
2698 		@RequestMapping(method = RequestMethod.GET)
2699 		public void handle(@CookieValue("date") Date date, Writer writer) throws IOException {
2700 			assertEquals("Invalid path variable value", new GregorianCalendar(2008, 10, 18).getTime(), date);
2701 			writer.write("test-" + new SimpleDateFormat("yyyy").format(date));
2702 		}
2703 	}
2704 
2705 	public interface TestController<T> {
2706 
2707 		ModelAndView method(T object);
2708 	}
2709 
2710 	public static class MyEntity {
2711 
2712 	}
2713 
2714 	@Controller
2715 	public static class TestControllerImpl implements TestController<MyEntity> {
2716 
2717 		@Override
2718 		@RequestMapping("/method")
2719 		public ModelAndView method(MyEntity object) {
2720 			return new ModelAndView("/something");
2721 		}
2722 	}
2723 
2724 	@Controller
2725 	public static class RequestParamMapController {
2726 
2727 		@RequestMapping("/map")
2728 		public void map(@RequestParam Map<String, String> params, Writer writer) throws IOException {
2729 			for (Iterator<Map.Entry<String, String>> it = params.entrySet().iterator(); it.hasNext();) {
2730 				Map.Entry<String, String> entry = it.next();
2731 				writer.write(entry.getKey() + "=" + entry.getValue());
2732 				if (it.hasNext()) {
2733 					writer.write(',');
2734 				}
2735 
2736 			}
2737 		}
2738 
2739 		@RequestMapping("/multiValueMap")
2740 		public void multiValueMap(@RequestParam MultiValueMap<String, String> params, Writer writer)
2741 				throws IOException {
2742 			for (Iterator<Map.Entry<String, List<String>>> it1 = params.entrySet().iterator(); it1.hasNext();) {
2743 				Map.Entry<String, List<String>> entry = it1.next();
2744 				writer.write(entry.getKey() + "=[");
2745 				for (Iterator<String> it2 = entry.getValue().iterator(); it2.hasNext();) {
2746 					String value = it2.next();
2747 					writer.write(value);
2748 					if (it2.hasNext()) {
2749 						writer.write(',');
2750 					}
2751 				}
2752 				writer.write(']');
2753 				if (it1.hasNext()) {
2754 					writer.write(',');
2755 				}
2756 			}
2757 		}
2758 	}
2759 
2760 	@Controller
2761 	public static class RequestHeaderMapController {
2762 
2763 		@RequestMapping("/map")
2764 		public void map(@RequestHeader Map<String, String> headers, Writer writer) throws IOException {
2765 			for (Iterator<Map.Entry<String, String>> it = headers.entrySet().iterator(); it.hasNext();) {
2766 				Map.Entry<String, String> entry = it.next();
2767 				writer.write(entry.getKey() + "=" + entry.getValue());
2768 				if (it.hasNext()) {
2769 					writer.write(',');
2770 				}
2771 
2772 			}
2773 		}
2774 
2775 		@RequestMapping("/multiValueMap")
2776 		public void multiValueMap(@RequestHeader MultiValueMap<String, String> headers, Writer writer)
2777 				throws IOException {
2778 			for (Iterator<Map.Entry<String, List<String>>> it1 = headers.entrySet().iterator(); it1.hasNext();) {
2779 				Map.Entry<String, List<String>> entry = it1.next();
2780 				writer.write(entry.getKey() + "=[");
2781 				for (Iterator<String> it2 = entry.getValue().iterator(); it2.hasNext();) {
2782 					String value = it2.next();
2783 					writer.write(value);
2784 					if (it2.hasNext()) {
2785 						writer.write(',');
2786 					}
2787 				}
2788 				writer.write(']');
2789 				if (it1.hasNext()) {
2790 					writer.write(',');
2791 				}
2792 			}
2793 		}
2794 
2795 		@RequestMapping("/httpHeaders")
2796 		public void httpHeaders(@RequestHeader HttpHeaders headers, Writer writer) throws IOException {
2797 			assertEquals("Invalid Content-Type", new MediaType("text", "html"), headers.getContentType());
2798 			multiValueMap(headers, writer);
2799 		}
2800 
2801 	}
2802 
2803 	@Controller
2804 	public interface IMyController {
2805 
2806 		@RequestMapping("/handle")
2807 		void handle(Writer writer, @RequestParam(value="p", required=false) String param) throws IOException;
2808 	}
2809 
2810 	@Controller
2811 	public static class IMyControllerImpl implements IMyController {
2812 
2813 		@Override
2814 		public void handle(Writer writer, @RequestParam(value="p", required=false) String param) throws IOException {
2815 			writer.write("handle " + param);
2816 		}
2817 	}
2818 
2819 	public static abstract class MyAbstractController {
2820 
2821 		@RequestMapping("/handle")
2822 		public abstract void handle(Writer writer) throws IOException;
2823 	}
2824 
2825 	@Controller
2826 	public static class MyAbstractControllerImpl extends MyAbstractController {
2827 
2828 		@Override
2829 		public void handle(Writer writer) throws IOException {
2830 			writer.write("handle");
2831 		}
2832 	}
2833 
2834 	@Controller
2835 	public static class TrailingSlashController  {
2836 
2837 		@RequestMapping(value = "/", method = RequestMethod.GET)
2838 		public void root(Writer writer) throws IOException {
2839 			writer.write("root");
2840 		}
2841 
2842 		@RequestMapping(value = "/{templatePath}/", method = RequestMethod.GET)
2843 		public void templatePath(Writer writer) throws IOException {
2844 			writer.write("templatePath");
2845 		}
2846 	}
2847 
2848 	@Controller
2849 	public static class ResponseEntityController {
2850 
2851 		@RequestMapping("/foo")
2852 		public ResponseEntity<String> foo(HttpEntity<byte[]> requestEntity) throws UnsupportedEncodingException {
2853 			assertNotNull(requestEntity);
2854 			assertEquals("MyValue", requestEntity.getHeaders().getFirst("MyRequestHeader"));
2855 			String requestBody = new String(requestEntity.getBody(), "UTF-8");
2856 			assertEquals("Hello World", requestBody);
2857 
2858 			HttpHeaders responseHeaders = new HttpHeaders();
2859 			responseHeaders.set("MyResponseHeader", "MyValue");
2860 			return new ResponseEntity<String>(requestBody, responseHeaders, HttpStatus.CREATED);
2861 		}
2862 
2863 		@RequestMapping("/bar")
2864 		public ResponseEntity<String> bar() {
2865 			HttpHeaders responseHeaders = new HttpHeaders();
2866 			responseHeaders.set("MyResponseHeader", "MyValue");
2867 			return new ResponseEntity<String>(responseHeaders, HttpStatus.NOT_FOUND);
2868 		}
2869 
2870 	}
2871 
2872 	@Controller
2873 	public static class CustomMapEditorController {
2874 
2875 		@InitBinder
2876 		public void initBinder(WebDataBinder binder) {
2877 			binder.initBeanPropertyAccess();
2878 			binder.registerCustomEditor(Map.class, new CustomMapEditor());
2879 		}
2880 
2881 		@SuppressWarnings("rawtypes")
2882 		@RequestMapping("/handle")
2883 		public void handle(@RequestParam("map") Map map, Writer writer) throws IOException {
2884 			writer.write("test-" + map);
2885 		}
2886 	}
2887 
2888 	public static class CustomMapEditor extends PropertyEditorSupport {
2889 
2890 		@Override
2891 		public void setAsText(String text) throws IllegalArgumentException {
2892 			if (StringUtils.hasText(text)) {
2893 				setValue(Collections.singletonMap("foo", text));
2894 			}
2895 			else {
2896 				setValue(null);
2897 			}
2898 		}
2899 
2900 	}
2901 
2902 	@Controller
2903 	public static class MultipartController {
2904 
2905 		@InitBinder
2906 		public void initBinder(WebDataBinder binder) {
2907 			binder.registerCustomEditor(String.class, new StringMultipartFileEditor());
2908 		}
2909 
2910 		@RequestMapping("/singleString")
2911 		public void processMultipart(@RequestParam("content") String content, HttpServletResponse response) throws IOException {
2912 			response.getWriter().write(content);
2913 		}
2914 
2915 		@RequestMapping("/stringArray")
2916 		public void processMultipart(@RequestParam("content") String[] content, HttpServletResponse response) throws IOException {
2917 			response.getWriter().write(StringUtils.arrayToDelimitedString(content, "-"));
2918 		}
2919 	}
2920 
2921 	@Controller
2922 	public static class CsvController {
2923 
2924 		@RequestMapping("/singleInteger")
2925 		public void processCsv(@RequestParam("content") Integer content, HttpServletResponse response) throws IOException {
2926 			response.getWriter().write(content.toString());
2927 		}
2928 
2929 		@RequestMapping("/integerArray")
2930 		public void processCsv(@RequestParam("content") Integer[] content, HttpServletResponse response) throws IOException {
2931 			response.getWriter().write(StringUtils.arrayToDelimitedString(content, "-"));
2932 		}
2933 	}
2934 
2935 	@Controller
2936 	@RequestMapping("/t1")
2937 	protected static class NoPathGetAndM2PostController {
2938 		@RequestMapping(method = RequestMethod.GET)
2939 		public void handle1(Writer writer) throws IOException {
2940 			writer.write("handle1");
2941 		}
2942 
2943 		@RequestMapping(value = "/m2", method = RequestMethod.POST)
2944 		public void handle2(Writer writer) throws IOException {
2945 			writer.write("handle2");
2946 		}
2947 	}
2948 
2949 	@Controller
2950 	static class HeadersConditionController {
2951 
2952 		@RequestMapping(value = "/", method = RequestMethod.GET)
2953 		public String home() {
2954 			return "home";
2955 		}
2956 
2957 		@RequestMapping(value = "/", method = RequestMethod.GET, headers="Accept=application/json")
2958 		@ResponseBody
2959 		public String homeJson() {
2960 			return "homeJson";
2961 		}
2962 	}
2963 
2964 	@Controller
2965 	static class RedirectAttributesController {
2966 
2967 		@InitBinder
2968 		public void initBinder(WebDataBinder dataBinder) {
2969 			dataBinder.setRequiredFields("name");
2970 		}
2971 
2972 		@RequestMapping(value = "/messages/{id}", method = RequestMethod.GET)
2973 		public void message(ModelMap model, Writer writer) throws IOException {
2974 			writer.write("Got: " + model.get("successMessage"));
2975 		}
2976 
2977 		@RequestMapping(value = "/messages", method = RequestMethod.POST)
2978 		public String sendMessage(TestBean testBean, BindingResult result, RedirectAttributes redirectAttrs) {
2979 			if (result.hasErrors()) {
2980 				return "messages/new";
2981 			}
2982 			else {
2983 				redirectAttrs.addAttribute("id", "1").addAttribute("name", "value")
2984 						.addFlashAttribute("successMessage", "yay!");
2985 				return "redirect:/messages/{id}";
2986 			}
2987 		}
2988 	}
2989 
2990 	@Controller
2991 	static class PrototypeController {
2992 
2993 		private int count;
2994 
2995 		@InitBinder
2996 		public void initBinder(WebDataBinder dataBinder) {
2997 			this.count++;
2998 		}
2999 
3000 		@ModelAttribute
3001 		public void populate(Model model) {
3002 			this.count++;
3003 		}
3004 
3005 		@RequestMapping("/")
3006 		public void message(int param, Writer writer) throws IOException {
3007 			this.count++;
3008 			writer.write("count:" + this.count);
3009 		}
3010 	}
3011 
3012 	@RestController
3013 	static class ThisWillActuallyRun {
3014 
3015 		@RequestMapping(value = "/", method = RequestMethod.GET)
3016 		public String home() {
3017 			return "Hello World!";
3018 		}
3019 	}
3020 
3021 	@Controller
3022 	static class HttpHeadersResponseController {
3023 
3024 		@RequestMapping(value = "", method = RequestMethod.POST)
3025 		@ResponseStatus(HttpStatus.CREATED)
3026 		public HttpHeaders create() throws URISyntaxException {
3027 			HttpHeaders headers = new HttpHeaders();
3028 			headers.setLocation(new URI("/test/items/123"));
3029 			return headers;
3030 		}
3031 
3032 		@RequestMapping(value = "empty", method = RequestMethod.POST)
3033 		@ResponseStatus(HttpStatus.CREATED)
3034 		public HttpHeaders createNoHeader() throws URISyntaxException {
3035 			return new HttpHeaders();
3036 		}
3037 
3038 	}
3039 
3040 
3041 // Test cases deleted from the original ServletAnnotationControllerTests:
3042 
3043 //	@Ignore("Controller interface => no method-level @RequestMapping annotation")
3044 //	public void standardHandleMethod() throws Exception {
3045 
3046 //	@Ignore("ControllerClassNameHandlerMapping")
3047 //	public void emptyRequestMapping() throws Exception {
3048 
3049 //	@Ignore("Controller interface => no method-level @RequestMapping annotation")
3050 //	public void proxiedStandardHandleMethod() throws Exception {
3051 
3052 //	@Ignore("ServletException no longer thrown for unmatched parameter constraints")
3053 //	public void constrainedParameterDispatchingController() throws Exception {
3054 
3055 //	@Ignore("Method name dispatching")
3056 //	public void methodNameDispatchingController() throws Exception {
3057 
3058 //	@Ignore("Method name dispatching")
3059 //	public void methodNameDispatchingControllerWithSuffix() throws Exception {
3060 
3061 //	@Ignore("ControllerClassNameHandlerMapping")
3062 //	public void controllerClassNamePlusMethodNameDispatchingController() throws Exception {
3063 
3064 //	@Ignore("Method name dispatching")
3065 //	public void postMethodNameDispatchingController() throws Exception {
3066 
3067 //	@Ignore("ControllerClassNameHandlerMapping")
3068 //	public void controllerClassNameNoTypeLevelAnn() throws Exception {
3069 
3070 //	@Ignore("SimpleUrlHandlerMapping")
3071 //	public void simpleUrlHandlerMapping() throws Exception {
3072 
3073 }