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