1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.web.servlet.config.annotation;
18
19 import java.util.List;
20 import java.util.Locale;
21 import java.util.Map;
22 import javax.servlet.http.HttpServletRequest;
23
24 import com.fasterxml.jackson.databind.DeserializationFeature;
25 import com.fasterxml.jackson.databind.MapperFeature;
26 import com.fasterxml.jackson.databind.ObjectMapper;
27 import com.fasterxml.jackson.dataformat.xml.XmlMapper;
28 import org.joda.time.DateTime;
29 import org.junit.Test;
30
31 import org.springframework.beans.DirectFieldAccessor;
32 import org.springframework.beans.factory.BeanFactoryUtils;
33 import org.springframework.context.ApplicationContext;
34 import org.springframework.context.MessageSource;
35 import org.springframework.context.annotation.Bean;
36 import org.springframework.context.annotation.Configuration;
37 import org.springframework.context.annotation.Scope;
38 import org.springframework.context.annotation.ScopedProxyMode;
39 import org.springframework.context.i18n.LocaleContextHolder;
40 import org.springframework.context.support.StaticMessageSource;
41 import org.springframework.core.Ordered;
42 import org.springframework.core.convert.ConversionService;
43 import org.springframework.format.annotation.DateTimeFormat;
44 import org.springframework.format.annotation.DateTimeFormat.ISO;
45 import org.springframework.format.support.FormattingConversionService;
46 import org.springframework.http.HttpEntity;
47 import org.springframework.http.HttpStatus;
48 import org.springframework.http.converter.HttpMessageConverter;
49 import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
50 import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
51 import org.springframework.mock.web.test.MockHttpServletRequest;
52 import org.springframework.mock.web.test.MockHttpServletResponse;
53 import org.springframework.mock.web.test.MockServletContext;
54 import org.springframework.stereotype.Controller;
55 import org.springframework.util.AntPathMatcher;
56 import org.springframework.util.PathMatcher;
57 import org.springframework.validation.Validator;
58 import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
59 import org.springframework.web.bind.annotation.PathVariable;
60 import org.springframework.web.bind.annotation.RequestMapping;
61 import org.springframework.web.bind.annotation.ResponseStatus;
62 import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
63 import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
64 import org.springframework.web.method.support.CompositeUriComponentsContributor;
65 import org.springframework.web.servlet.HandlerExceptionResolver;
66 import org.springframework.web.servlet.HandlerExecutionChain;
67 import org.springframework.web.servlet.ViewResolver;
68 import org.springframework.web.servlet.handler.AbstractHandlerMapping;
69 import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
70 import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor;
71 import org.springframework.web.servlet.handler.HandlerExceptionResolverComposite;
72 import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
73 import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
74 import org.springframework.web.servlet.mvc.method.annotation.JsonViewResponseBodyAdvice;
75 import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
76 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
77 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
78 import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
79 import org.springframework.web.servlet.resource.ResourceUrlProviderExposingInterceptor;
80 import org.springframework.web.servlet.view.BeanNameViewResolver;
81 import org.springframework.web.servlet.view.InternalResourceViewResolver;
82 import org.springframework.web.servlet.view.ViewResolverComposite;
83 import org.springframework.web.util.UrlPathHelper;
84
85 import static org.junit.Assert.*;
86
87
88
89
90
91
92
93
94 public class WebMvcConfigurationSupportTests {
95
96 @Test
97 public void requestMappingHandlerMapping() throws Exception {
98 ApplicationContext context = initContext(WebConfig.class, ScopedController.class, ScopedProxyController.class);
99 RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
100 assertEquals(0, handlerMapping.getOrder());
101
102 HandlerExecutionChain chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/"));
103 assertNotNull(chain);
104 assertNotNull(chain.getInterceptors());
105 assertEquals(ConversionServiceExposingInterceptor.class, chain.getInterceptors()[0].getClass());
106
107 chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/scoped"));
108 assertNotNull(chain);
109
110 chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/scopedProxy"));
111 assertNotNull(chain);
112 }
113
114 @Test
115 public void emptyViewControllerHandlerMapping() {
116 ApplicationContext context = initContext(WebConfig.class);
117 String name = "viewControllerHandlerMapping";
118 AbstractHandlerMapping handlerMapping = context.getBean(name, AbstractHandlerMapping.class);
119
120 assertNotNull(handlerMapping);
121 assertEquals(Integer.MAX_VALUE, handlerMapping.getOrder());
122 assertTrue(handlerMapping.getClass().getName().endsWith("EmptyHandlerMapping"));
123 }
124
125 @Test
126 public void beanNameHandlerMapping() throws Exception {
127 ApplicationContext context = initContext(WebConfig.class);
128 BeanNameUrlHandlerMapping handlerMapping = context.getBean(BeanNameUrlHandlerMapping.class);
129 assertEquals(2, handlerMapping.getOrder());
130
131 HttpServletRequest request = new MockHttpServletRequest("GET", "/testController");
132 HandlerExecutionChain chain = handlerMapping.getHandler(request);
133
134 assertNotNull(chain.getInterceptors());
135 assertEquals(3, chain.getInterceptors().length);
136 assertEquals(ConversionServiceExposingInterceptor.class, chain.getInterceptors()[1].getClass());
137 assertEquals(ResourceUrlProviderExposingInterceptor.class, chain.getInterceptors()[2].getClass());
138 }
139
140 @Test
141 public void emptyResourceHandlerMapping() {
142 ApplicationContext context = initContext(WebConfig.class);
143 AbstractHandlerMapping handlerMapping = context.getBean("resourceHandlerMapping", AbstractHandlerMapping.class);
144
145 assertNotNull(handlerMapping);
146 assertEquals(Integer.MAX_VALUE, handlerMapping.getOrder());
147 assertTrue(handlerMapping.getClass().getName().endsWith("EmptyHandlerMapping"));
148 }
149
150 @Test
151 public void emptyDefaultServletHandlerMapping() {
152 ApplicationContext context = initContext(WebConfig.class);
153 String name = "defaultServletHandlerMapping";
154 AbstractHandlerMapping handlerMapping = context.getBean(name, AbstractHandlerMapping.class);
155
156 assertNotNull(handlerMapping);
157 assertEquals(Integer.MAX_VALUE, handlerMapping.getOrder());
158 assertTrue(handlerMapping.getClass().getName().endsWith("EmptyHandlerMapping"));
159 }
160
161 @Test
162 public void requestMappingHandlerAdapter() throws Exception {
163 ApplicationContext context = initContext(WebConfig.class);
164 RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);
165 List<HttpMessageConverter<?>> converters = adapter.getMessageConverters();
166 assertEquals(9, converters.size());
167 for(HttpMessageConverter<?> converter : converters) {
168 if (converter instanceof AbstractJackson2HttpMessageConverter) {
169 ObjectMapper objectMapper = ((AbstractJackson2HttpMessageConverter)converter).getObjectMapper();
170 assertFalse(objectMapper.getDeserializationConfig().isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION));
171 assertFalse(objectMapper.getSerializationConfig().isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION));
172 assertFalse(objectMapper.getDeserializationConfig().isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
173 if (converter instanceof MappingJackson2XmlHttpMessageConverter) {
174 assertEquals(XmlMapper.class, objectMapper.getClass());
175 }
176 }
177 }
178
179 ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) adapter.getWebBindingInitializer();
180 assertNotNull(initializer);
181
182 ConversionService conversionService = initializer.getConversionService();
183 assertNotNull(conversionService);
184 assertTrue(conversionService instanceof FormattingConversionService);
185
186 Validator validator = initializer.getValidator();
187 assertNotNull(validator);
188 assertTrue(validator instanceof LocalValidatorFactoryBean);
189
190 DirectFieldAccessor fieldAccessor = new DirectFieldAccessor(adapter);
191 List<Object> interceptors = (List<Object>) fieldAccessor.getPropertyValue("responseBodyAdvice");
192 assertEquals(1, interceptors.size());
193 assertEquals(JsonViewResponseBodyAdvice.class, interceptors.get(0).getClass());
194 }
195
196 @Test
197 public void uriComponentsContributor() throws Exception {
198 ApplicationContext context = initContext(WebConfig.class);
199 CompositeUriComponentsContributor uriComponentsContributor = context.getBean(
200 MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME,
201 CompositeUriComponentsContributor.class);
202
203 assertNotNull(uriComponentsContributor);
204 }
205
206 @Test
207 public void handlerExceptionResolver() throws Exception {
208 ApplicationContext context = initContext(WebConfig.class);
209 HandlerExceptionResolverComposite compositeResolver =
210 context.getBean("handlerExceptionResolver", HandlerExceptionResolverComposite.class);
211
212 assertEquals(0, compositeResolver.getOrder());
213 List<HandlerExceptionResolver> expectedResolvers = compositeResolver.getExceptionResolvers();
214
215 assertEquals(ExceptionHandlerExceptionResolver.class, expectedResolvers.get(0).getClass());
216 assertEquals(ResponseStatusExceptionResolver.class, expectedResolvers.get(1).getClass());
217 assertEquals(DefaultHandlerExceptionResolver.class, expectedResolvers.get(2).getClass());
218
219 ExceptionHandlerExceptionResolver eher = (ExceptionHandlerExceptionResolver) expectedResolvers.get(0);
220 assertNotNull(eher.getApplicationContext());
221
222 DirectFieldAccessor fieldAccessor = new DirectFieldAccessor(eher);
223 List<Object> interceptors = (List<Object>) fieldAccessor.getPropertyValue("responseBodyAdvice");
224 assertEquals(1, interceptors.size());
225 assertEquals(JsonViewResponseBodyAdvice.class, interceptors.get(0).getClass());
226
227 LocaleContextHolder.setLocale(Locale.ENGLISH);
228 try {
229 ResponseStatusExceptionResolver rser = (ResponseStatusExceptionResolver) expectedResolvers.get(1);
230 MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
231 MockHttpServletResponse response = new MockHttpServletResponse();
232 rser.resolveException(request, response, context.getBean(TestController.class), new UserAlreadyExistsException());
233 assertEquals("User already exists!", response.getErrorMessage());
234 }
235 finally {
236 LocaleContextHolder.resetLocaleContext();
237 }
238 }
239
240 @Test
241 public void mvcViewResolver() {
242 ApplicationContext context = initContext(WebConfig.class);
243 ViewResolverComposite resolver = context.getBean("mvcViewResolver", ViewResolverComposite.class);
244
245 assertNotNull(resolver);
246 assertEquals(1, resolver.getViewResolvers().size());
247 assertEquals(InternalResourceViewResolver.class, resolver.getViewResolvers().get(0).getClass());
248 assertEquals(Ordered.LOWEST_PRECEDENCE, resolver.getOrder());
249 }
250
251 @Test
252 public void mvcViewResolverWithExistingResolver() throws Exception {
253 ApplicationContext context = initContext(WebConfig.class, ViewResolverConfig.class);
254 ViewResolverComposite resolver = context.getBean("mvcViewResolver", ViewResolverComposite.class);
255
256 assertNotNull(resolver);
257 assertEquals(0, resolver.getViewResolvers().size());
258 assertEquals(Ordered.LOWEST_PRECEDENCE, resolver.getOrder());
259 assertNull(resolver.resolveViewName("anyViewName", Locale.ENGLISH));
260 }
261
262 @Test
263 public void mvcViewResolverWithOrderSet() {
264 ApplicationContext context = initContext(CustomViewResolverOrderConfig.class);
265 ViewResolverComposite resolver = context.getBean("mvcViewResolver", ViewResolverComposite.class);
266
267 Map<String, ViewResolver> map = BeanFactoryUtils.beansOfTypeIncludingAncestors(
268 context, ViewResolver.class, true, false);
269
270 assertNotNull(resolver);
271 assertEquals(1, resolver.getViewResolvers().size());
272 assertEquals(InternalResourceViewResolver.class, resolver.getViewResolvers().get(0).getClass());
273 assertEquals(123, resolver.getOrder());
274 }
275
276 @Test
277 public void defaultPathMatchConfiguration() throws Exception {
278 ApplicationContext context = initContext(WebConfig.class);
279 UrlPathHelper urlPathHelper = context.getBean(UrlPathHelper.class);
280 PathMatcher pathMatcher = context.getBean(PathMatcher.class);
281
282 assertNotNull(urlPathHelper);
283 assertNotNull(pathMatcher);
284 assertEquals(AntPathMatcher.class, pathMatcher.getClass());
285 }
286
287
288 private ApplicationContext initContext(Class... configClasses) {
289 AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
290 context.setServletContext(new MockServletContext());
291 context.register(configClasses);
292 context.refresh();
293 return context;
294 }
295
296
297 @EnableWebMvc
298 @Configuration
299 public static class WebConfig {
300
301 @Bean(name="/testController")
302 public TestController testController() {
303 return new TestController();
304 }
305
306 @Bean
307 public MessageSource messageSource() {
308 StaticMessageSource messageSource = new StaticMessageSource();
309 messageSource.addMessage("exception.user.exists", Locale.ENGLISH, "User already exists!");
310 return messageSource;
311 }
312 }
313
314
315 @Configuration
316 public static class ViewResolverConfig {
317
318 @Bean
319 public ViewResolver beanNameViewResolver() {
320 return new BeanNameViewResolver();
321 }
322 }
323
324
325 @EnableWebMvc
326 @Configuration
327 public static class CustomViewResolverOrderConfig extends WebMvcConfigurerAdapter {
328
329 @Override
330 public void configureViewResolvers(ViewResolverRegistry registry) {
331 registry.jsp();
332 registry.order(123);
333 }
334 }
335
336
337 @Controller
338 public static class TestController {
339
340 @RequestMapping("/")
341 public void handle() {
342 }
343
344 @RequestMapping("/foo/{id}/bar/{date}")
345 public HttpEntity<Void> methodWithTwoPathVariables(@PathVariable Integer id,
346 @DateTimeFormat(iso = ISO.DATE) @PathVariable DateTime date) {
347 return null;
348 }
349 }
350
351
352 @Controller
353 @Scope("prototype")
354 public static class ScopedController {
355
356 @RequestMapping("/scoped")
357 public void handle() {
358 }
359 }
360
361
362 @Controller
363 @Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)
364 public static class ScopedProxyController {
365
366 @RequestMapping("/scopedProxy")
367 public void handle() {
368 }
369 }
370
371
372 @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "exception.user.exists")
373 @SuppressWarnings("serial")
374 public static class UserAlreadyExistsException extends RuntimeException {
375 }
376
377 }