diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java index 6bff5bd8d2..ae7a851d00 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,6 +61,10 @@ public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionRes logger.warn("Handling of @ResponseStatus resulted in Exception", resolveEx); } } + else if (ex.getCause() != null && ex.getCause() instanceof Exception) { + ex = (Exception) ex.getCause(); + return doResolveException(request, response, handler, ex); + } return null; } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolverTests.java index 961114c831..99b1fc682c 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,21 +16,23 @@ package org.springframework.web.servlet.mvc.annotation; +import static org.junit.Assert.*; + import java.util.Locale; import org.junit.Before; import org.junit.Test; +import org.springframework.beans.TypeMismatchException; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.support.StaticMessageSource; import org.springframework.http.HttpStatus; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; +import org.springframework.tests.sample.beans.ITestBean; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.servlet.ModelAndView; -import static org.junit.Assert.*; - /** @author Arjen Poutsma */ public class ResponseStatusExceptionResolverTests { @@ -93,6 +95,18 @@ public class ResponseStatusExceptionResolverTests { assertNull("ModelAndView returned", mav); } + // SPR-12903 + + @Test + public void nestedException() throws Exception { + Exception cause = new StatusCodeAndReasonMessageException(); + TypeMismatchException ex = new TypeMismatchException("value", ITestBean.class, cause); + ModelAndView mav = exceptionResolver.resolveException(request, response, null, ex); + assertNotNull("No ModelAndView returned", mav); + assertTrue("No Empty ModelAndView returned", mav.isEmpty()); + assertEquals("Invalid status code", 410, response.getStatus()); + } + @ResponseStatus(HttpStatus.BAD_REQUEST) @SuppressWarnings("serial") private static class StatusCodeException extends Exception { diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java index e7d7e1abc0..d944c46700 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java @@ -113,6 +113,7 @@ import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; @@ -284,6 +285,29 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl assertEquals("[1, 2]-org.springframework.tests.sample.beans.TestBean", response.getContentAsString()); } + // SPR-12903 + + @Test + public void pathVariableWithCustomConverter() throws Exception { + initServlet(new ApplicationContextInitializer() { + @Override + public void initialize(GenericWebApplicationContext context) { + RootBeanDefinition csDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class); + csDef.getPropertyValues().add("converters", new AnnotatedExceptionRaisingConverter()); + RootBeanDefinition wbiDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class); + wbiDef.getPropertyValues().add("conversionService", csDef); + RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class); + adapterDef.getPropertyValues().add("webBindingInitializer", wbiDef); + context.registerBeanDefinition("handlerAdapter", adapterDef); + } + }, PathVariableWithCustomConverterController.class); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath/1"); + MockHttpServletResponse response = new MockHttpServletResponse(); + getServlet().service(request, response); + assertEquals(404, response.getStatus()); + } + @Test public void methodNotAllowed() throws Exception { initServletWithControllers(MethodNotAllowedController.class); @@ -2370,6 +2394,26 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl } } + @Controller + public static class PathVariableWithCustomConverterController { + + @RequestMapping("/myPath/{id}") + public void myHandle(@PathVariable("id") ITestBean bean) throws Exception { + } + } + + public static class AnnotatedExceptionRaisingConverter implements Converter { + + @Override + public ITestBean convert(String source) { + throw new NotFoundException(); + } + + @ResponseStatus(HttpStatus.NOT_FOUND) + private static class NotFoundException extends RuntimeException { + } + } + @Controller public static class MethodNotAllowedController {