1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.web.servlet.mvc.method.annotation;
18
19 import java.io.IOException;
20 import java.io.UnsupportedEncodingException;
21 import java.io.Writer;
22 import java.util.Arrays;
23
24 import org.junit.Before;
25 import org.junit.BeforeClass;
26 import org.junit.Test;
27
28 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
29 import org.springframework.context.annotation.Bean;
30 import org.springframework.context.annotation.Configuration;
31 import org.springframework.core.annotation.Order;
32 import org.springframework.mock.web.test.MockHttpServletRequest;
33 import org.springframework.mock.web.test.MockHttpServletResponse;
34 import org.springframework.stereotype.Controller;
35 import org.springframework.util.ClassUtils;
36 import org.springframework.web.bind.annotation.ControllerAdvice;
37 import org.springframework.web.bind.annotation.ExceptionHandler;
38 import org.springframework.web.bind.annotation.ResponseBody;
39 import org.springframework.web.method.HandlerMethod;
40 import org.springframework.web.method.annotation.ModelMethodProcessor;
41 import org.springframework.web.method.support.HandlerMethodArgumentResolver;
42 import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
43 import org.springframework.web.servlet.ModelAndView;
44
45 import static org.junit.Assert.*;
46
47
48
49
50
51
52
53
54 public class ExceptionHandlerExceptionResolverTests {
55
56 private static int RESOLVER_COUNT;
57
58 private static int HANDLER_COUNT;
59
60 private ExceptionHandlerExceptionResolver resolver;
61
62 private MockHttpServletRequest request;
63
64 private MockHttpServletResponse response;
65
66 @BeforeClass
67 public static void setupOnce() {
68 ExceptionHandlerExceptionResolver r = new ExceptionHandlerExceptionResolver();
69 r.afterPropertiesSet();
70 RESOLVER_COUNT = r.getArgumentResolvers().getResolvers().size();
71 HANDLER_COUNT = r.getReturnValueHandlers().getHandlers().size();
72 }
73
74 @Before
75 public void setUp() throws Exception {
76 this.resolver = new ExceptionHandlerExceptionResolver();
77 this.request = new MockHttpServletRequest("GET", "/");
78 this.response = new MockHttpServletResponse();
79 }
80
81 @Test
82 public void nullHandler() {
83 Object handler = null;
84 this.resolver.afterPropertiesSet();
85 ModelAndView mav = this.resolver.resolveException(this.request, this.response, handler, null);
86 assertNull("Exception can be resolved only if there is a HandlerMethod", mav);
87 }
88
89 @Test
90 public void setCustomArgumentResolvers() throws Exception {
91 HandlerMethodArgumentResolver resolver = new ServletRequestMethodArgumentResolver();
92 this.resolver.setCustomArgumentResolvers(Arrays.asList(resolver));
93 this.resolver.afterPropertiesSet();
94
95 assertTrue(this.resolver.getArgumentResolvers().getResolvers().contains(resolver));
96 assertMethodProcessorCount(RESOLVER_COUNT + 1, HANDLER_COUNT);
97 }
98
99 @Test
100 public void setArgumentResolvers() throws Exception {
101 HandlerMethodArgumentResolver resolver = new ServletRequestMethodArgumentResolver();
102 this.resolver.setArgumentResolvers(Arrays.asList(resolver));
103 this.resolver.afterPropertiesSet();
104
105 assertMethodProcessorCount(1, HANDLER_COUNT);
106 }
107
108 @Test
109 public void setCustomReturnValueHandlers() {
110 HandlerMethodReturnValueHandler handler = new ViewNameMethodReturnValueHandler();
111 this.resolver.setCustomReturnValueHandlers(Arrays.asList(handler));
112 this.resolver.afterPropertiesSet();
113
114 assertTrue(this.resolver.getReturnValueHandlers().getHandlers().contains(handler));
115 assertMethodProcessorCount(RESOLVER_COUNT, HANDLER_COUNT + 1);
116 }
117
118 @Test
119 public void setReturnValueHandlers() {
120 HandlerMethodReturnValueHandler handler = new ModelMethodProcessor();
121 this.resolver.setReturnValueHandlers(Arrays.asList(handler));
122 this.resolver.afterPropertiesSet();
123
124 assertMethodProcessorCount(RESOLVER_COUNT, 1);
125 }
126
127 @Test
128 public void resolveNoExceptionHandlerForException() throws NoSuchMethodException {
129 Exception npe = new NullPointerException();
130 HandlerMethod handlerMethod = new HandlerMethod(new IoExceptionController(), "handle");
131 this.resolver.afterPropertiesSet();
132 ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, npe);
133
134 assertNull("NPE should not have been handled", mav);
135 }
136
137 @Test
138 public void resolveExceptionModelAndView() throws NoSuchMethodException {
139 IllegalArgumentException ex = new IllegalArgumentException("Bad argument");
140 HandlerMethod handlerMethod = new HandlerMethod(new ModelAndViewController(), "handle");
141 this.resolver.afterPropertiesSet();
142 ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
143
144 assertNotNull(mav);
145 assertFalse(mav.isEmpty());
146 assertEquals("errorView", mav.getViewName());
147 assertEquals("Bad argument", mav.getModel().get("detail"));
148 }
149
150 @Test
151 public void resolveExceptionResponseBody() throws UnsupportedEncodingException, NoSuchMethodException {
152 IllegalArgumentException ex = new IllegalArgumentException();
153 HandlerMethod handlerMethod = new HandlerMethod(new ResponseBodyController(), "handle");
154 this.resolver.afterPropertiesSet();
155 ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
156
157 assertNotNull(mav);
158 assertTrue(mav.isEmpty());
159 assertEquals("IllegalArgumentException", this.response.getContentAsString());
160 }
161
162 @Test
163 public void resolveExceptionResponseWriter() throws Exception {
164 IllegalArgumentException ex = new IllegalArgumentException();
165 HandlerMethod handlerMethod = new HandlerMethod(new ResponseWriterController(), "handle");
166 this.resolver.afterPropertiesSet();
167 ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
168
169 assertNotNull(mav);
170 assertTrue(mav.isEmpty());
171 assertEquals("IllegalArgumentException", this.response.getContentAsString());
172 }
173
174 @Test
175 public void resolveExceptionGlobalHandler() throws Exception {
176 AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(MyConfig.class);
177 this.resolver.setApplicationContext(cxt);
178 this.resolver.afterPropertiesSet();
179
180 IllegalAccessException ex = new IllegalAccessException();
181 HandlerMethod handlerMethod = new HandlerMethod(new ResponseBodyController(), "handle");
182 ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
183
184 assertNotNull("Exception was not handled", mav);
185 assertTrue(mav.isEmpty());
186 assertEquals("AnotherTestExceptionResolver: IllegalAccessException", this.response.getContentAsString());
187 }
188
189 @Test
190 public void resolveExceptionGlobalHandlerOrdered() throws Exception {
191 AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(MyConfig.class);
192 this.resolver.setApplicationContext(cxt);
193 this.resolver.afterPropertiesSet();
194
195 IllegalStateException ex = new IllegalStateException();
196 HandlerMethod handlerMethod = new HandlerMethod(new ResponseBodyController(), "handle");
197 ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
198
199 assertNotNull("Exception was not handled", mav);
200 assertTrue(mav.isEmpty());
201 assertEquals("TestExceptionResolver: IllegalStateException", this.response.getContentAsString());
202 }
203
204 @Test
205 public void resolveExceptionControllerAdviceHandler() throws Exception {
206 AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(MyControllerAdviceConfig.class);
207 this.resolver.setApplicationContext(cxt);
208 this.resolver.afterPropertiesSet();
209
210 IllegalStateException ex = new IllegalStateException();
211 HandlerMethod handlerMethod = new HandlerMethod(new ResponseBodyController(), "handle");
212 ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
213
214 assertNotNull("Exception was not handled", mav);
215 assertTrue(mav.isEmpty());
216 assertEquals("BasePackageTestExceptionResolver: IllegalStateException", this.response.getContentAsString());
217 }
218
219 @Test
220 public void resolveExceptionControllerAdviceNoHandler() throws Exception {
221 AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(MyControllerAdviceConfig.class);
222 this.resolver.setApplicationContext(cxt);
223 this.resolver.afterPropertiesSet();
224
225 IllegalStateException ex = new IllegalStateException();
226 ModelAndView mav = this.resolver.resolveException(this.request, this.response, null, ex);
227
228 assertNotNull("Exception was not handled", mav);
229 assertTrue(mav.isEmpty());
230 assertEquals("DefaultTestExceptionResolver: IllegalStateException", this.response.getContentAsString());
231 }
232
233
234 private void assertMethodProcessorCount(int resolverCount, int handlerCount) {
235 assertEquals(resolverCount, this.resolver.getArgumentResolvers().getResolvers().size());
236 assertEquals(handlerCount, this.resolver.getReturnValueHandlers().getHandlers().size());
237 }
238
239 @Controller
240 static class ModelAndViewController {
241
242 public void handle() {}
243
244 @ExceptionHandler
245 public ModelAndView handle(Exception ex) throws IOException {
246 return new ModelAndView("errorView", "detail", ex.getMessage());
247 }
248 }
249
250 @Controller
251 static class ResponseWriterController {
252
253 public void handle() {}
254
255 @ExceptionHandler
256 public void handleException(Exception ex, Writer writer) throws IOException {
257 writer.write(ClassUtils.getShortName(ex.getClass()));
258 }
259 }
260
261 @Controller
262 static class ResponseBodyController {
263
264 public void handle() {}
265
266 @ExceptionHandler
267 @ResponseBody
268 public String handleException(IllegalArgumentException ex) {
269 return ClassUtils.getShortName(ex.getClass());
270 }
271 }
272
273 @Controller
274 static class IoExceptionController {
275
276 public void handle() {}
277
278 @ExceptionHandler(value=IOException.class)
279 public void handleException() {
280 }
281 }
282
283 @ControllerAdvice
284 @Order(1)
285 static class TestExceptionResolver {
286
287 @ExceptionHandler
288 @ResponseBody
289 public String handleException(IllegalStateException ex) {
290 return "TestExceptionResolver: " + ClassUtils.getShortName(ex.getClass());
291 }
292 }
293
294 @ControllerAdvice
295 @Order(2)
296 static class AnotherTestExceptionResolver {
297
298 @ExceptionHandler({IllegalStateException.class, IllegalAccessException.class})
299 @ResponseBody
300 public String handleException(Exception ex) {
301 return "AnotherTestExceptionResolver: " + ClassUtils.getShortName(ex.getClass());
302 }
303 }
304
305 @Configuration
306 static class MyConfig {
307
308 @Bean public TestExceptionResolver testExceptionResolver() {
309 return new TestExceptionResolver();
310 }
311
312 @Bean public AnotherTestExceptionResolver anotherTestExceptionResolver() {
313 return new AnotherTestExceptionResolver();
314 }
315 }
316
317 @ControllerAdvice("java.lang")
318 @Order(1)
319 static class NotCalledTestExceptionResolver {
320
321 @ExceptionHandler
322 @ResponseBody
323 public String handleException(IllegalStateException ex) {
324 return "NotCalledTestExceptionResolver: " + ClassUtils.getShortName(ex.getClass());
325 }
326 }
327
328 @ControllerAdvice("org.springframework.web.servlet.mvc.method.annotation")
329 @Order(2)
330 static class BasePackageTestExceptionResolver {
331
332 @ExceptionHandler
333 @ResponseBody
334 public String handleException(IllegalStateException ex) {
335 return "BasePackageTestExceptionResolver: " + ClassUtils.getShortName(ex.getClass());
336 }
337 }
338
339 @ControllerAdvice
340 @Order(3)
341 static class DefaultTestExceptionResolver {
342
343 @ExceptionHandler
344 @ResponseBody
345 public String handleException(IllegalStateException ex) {
346 return "DefaultTestExceptionResolver: " + ClassUtils.getShortName(ex.getClass());
347 }
348 }
349
350 @Configuration
351 static class MyControllerAdviceConfig {
352 @Bean public NotCalledTestExceptionResolver notCalledTestExceptionResolver() {
353 return new NotCalledTestExceptionResolver();
354 }
355
356 @Bean public BasePackageTestExceptionResolver basePackageTestExceptionResolver() {
357 return new BasePackageTestExceptionResolver();
358 }
359
360 @Bean public DefaultTestExceptionResolver defaultTestExceptionResolver() {
361 return new DefaultTestExceptionResolver();
362 }
363 }
364 }