diff --git a/exception-handler/src/main/java/com/raychatter/common/annotation/AnnotationHandler.java b/exception-handler/src/main/java/com/raychatter/common/annotation/AnnotationHandler.java index f88f3fd..353b05f 100644 --- a/exception-handler/src/main/java/com/raychatter/common/annotation/AnnotationHandler.java +++ b/exception-handler/src/main/java/com/raychatter/common/annotation/AnnotationHandler.java @@ -1,6 +1,8 @@ package com.raychatter.common.annotation; import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; @@ -12,23 +14,28 @@ import java.util.Scanner; public class AnnotationHandler implements HandlerExceptionResolver { - protected static final String DEFAULT_ERROR_STRING = "Error: %s"; + protected static final String DEFAULT_ERROR_STRING = "%s"; protected static final String USER_TEMPLATE = "error.template"; protected static final String DEFAULT_TEMPLATE = "defaults/default.template"; private static final String UTF_8 = "UTF-8"; @Override public ModelAndView resolveException(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception thrownException) { - final ExceptionHandler annotation = thrownException.getClass().getAnnotation(ExceptionHandler.class); + final ExceptionHandler annotation = getAnnotationFrom(thrownException); - if (annotation == null) { - return new ModelAndView(); + if (annotation == null) return new ModelAndView(); + + try { + return handleException(annotation, thrownException, response); + } catch (IOException e) { + // potentially something went wrong in response itself + e.printStackTrace(); } - return handleException(annotation, thrownException, response); + return new ModelAndView(); } - protected ModelAndView handleException(final ExceptionHandler annotation, final Exception thrownException, final HttpServletResponse response) { + protected ModelAndView handleException(final ExceptionHandler annotation, final Exception thrownException, final HttpServletResponse response) throws IOException { response.setContentType(annotation.contentType()); response.setStatus(annotation.httpStatus().value()); @@ -36,20 +43,18 @@ public class AnnotationHandler implements HandlerExceptionResolver { final String message = formatMessage(thrownException); response.getWriter().write(message); } catch (IOException e) { - //TODO: Potentially this can be handled differently than the template errors -// response.setContentType(MediaType.APPLICATION_XML_VALUE); -// response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); -// -// try { -// response.getWriter().write(formatDefaultMessage(thrownException)); -// } catch (IOException ex) { -// ex.printStackTrace(); -// } + response.setContentType(MediaType.APPLICATION_XML_VALUE); + response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.getWriter().write(formatDefaultMessage(thrownException)); } return new ModelAndView(); } + protected ExceptionHandler getAnnotationFrom(Exception thrownException) { + return thrownException.getClass().getAnnotation(ExceptionHandler.class); + } + protected String formatMessage(final Exception thrownException) throws IOException { return String.format(readTemplate(), thrownException.getMessage()); } diff --git a/exception-handler/src/main/java/com/raychatter/common/annotation/ExceptionHandler.java b/exception-handler/src/main/java/com/raychatter/common/annotation/ExceptionHandler.java index 3461b94..9e7dda9 100644 --- a/exception-handler/src/main/java/com/raychatter/common/annotation/ExceptionHandler.java +++ b/exception-handler/src/main/java/com/raychatter/common/annotation/ExceptionHandler.java @@ -19,5 +19,5 @@ import java.lang.annotation.Target; ElementType.TYPE}) public @interface ExceptionHandler { HttpStatus httpStatus() default HttpStatus.INTERNAL_SERVER_ERROR; - String contentType() default MediaType.TEXT_PLAIN_VALUE; + String contentType() default MediaType.APPLICATION_XML_VALUE; } diff --git a/exception-handler/src/test/java/com/raychatter/common/annotation/AnnotationHandlerTest.java b/exception-handler/src/test/java/com/raychatter/common/annotation/AnnotationHandlerTest.java index 7815180..83e35f4 100644 --- a/exception-handler/src/test/java/com/raychatter/common/annotation/AnnotationHandlerTest.java +++ b/exception-handler/src/test/java/com/raychatter/common/annotation/AnnotationHandlerTest.java @@ -4,6 +4,7 @@ import junit.framework.Assert; import org.junit.Test; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; @@ -12,12 +13,7 @@ import java.io.InputStream; import java.io.PrintWriter; import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; public class AnnotationHandlerTest { @@ -93,7 +89,8 @@ public class AnnotationHandlerTest { Assert.assertEquals(expectedDefaultTemplateString, actual); } - @Test public void handleException_ShouldRenderDefaultContentType_WhenNoAnnotationAttributesGiven() throws Exception { + @Test + public void handleException_ShouldRenderDefaultContentType_WhenNoAnnotationAttributesGiven() throws Exception { final String emptyString = ""; final HttpServletResponse mockResponse = mock(HttpServletResponse.class); @@ -106,10 +103,11 @@ public class AnnotationHandlerTest { sut.handleException(TestExceptionWithNoAnnotationAttributes.class.getAnnotation(ExceptionHandler.class), null, mockResponse); - verify(mockResponse).setContentType(MediaType.TEXT_PLAIN_VALUE); + verify(mockResponse).setContentType(MediaType.APPLICATION_XML_VALUE); } - @Test public void handleException_ShouldRenderDefaultHttpStatusCode_WhenNoAnnotationAttributesGiven() throws Exception { + @Test + public void handleException_ShouldRenderDefaultHttpStatusCode_WhenNoAnnotationAttributesGiven() throws Exception { final String emptyString = ""; final HttpServletResponse mockResponse = mock(HttpServletResponse.class); @@ -125,7 +123,8 @@ public class AnnotationHandlerTest { verify(mockResponse).setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); } - @Test public void handleException_ShouldRenderNotFoundHttpStatusCode_WhenNotFoundAnnotationAttributeIsGiven() throws Exception { + @Test + public void handleException_ShouldRenderNotFoundHttpStatusCode_WhenNotFoundAnnotationAttributeIsGiven() throws Exception { final String emptyString = ""; final HttpServletResponse mockResponse = mock(HttpServletResponse.class); @@ -141,7 +140,8 @@ public class AnnotationHandlerTest { verify(mockResponse).setStatus(HttpStatus.NOT_FOUND.value()); } - @Test public void handleException_ShouldRenderXmlContentType_WhenXmlContentTypeAnnotationAttributeIsGiven() throws Exception { + @Test + public void handleException_ShouldRenderXmlContentType_WhenXmlContentTypeAnnotationAttributeIsGiven() throws Exception { final String emptyString = ""; final HttpServletResponse mockResponse = mock(HttpServletResponse.class); @@ -157,7 +157,8 @@ public class AnnotationHandlerTest { verify(mockResponse).setContentType(MediaType.APPLICATION_XML_VALUE); } - @Test public void handleException_ShouldRenderUserMessage_WhenUserTemplateIsGiven() throws Exception { + @Test + public void handleException_ShouldRenderUserMessage_WhenUserTemplateIsGiven() throws Exception { final String expectedUserTemplate = "USER TEMPLATE: %s"; final String expectedErrorMessage = "ERROR MESSAGE"; final String expectedErrorBody = "USER TEMPLATE: ERROR MESSAGE"; @@ -175,6 +176,70 @@ public class AnnotationHandlerTest { verify(mockPrinter).write(expectedErrorBody); } + @Test + public void handleException_ShouldReturnXmlContentType_WhenNoUserTemplateGiven() throws Exception { + final String emptyString = ""; + final HttpServletResponse mockResponse = mock(HttpServletResponse.class); + final PrintWriter mockPrinter = mock(PrintWriter.class); + when(mockResponse.getWriter()).thenReturn(mockPrinter); + + final AnnotationHandler sut = spy(new AnnotationHandler()); + doThrow(IOException.class).when(sut).getResource(AnnotationHandler.USER_TEMPLATE); + doReturn(emptyString).when(sut).formatDefaultMessage(null); + + sut.handleException(TestExceptionWithNoContentStatusCodeAndTextContentType.class.getAnnotation(ExceptionHandler.class), null, mockResponse); + + verify(mockResponse).setContentType(MediaType.APPLICATION_XML_VALUE); + } + + @Test + public void handleException_ShouldReturnInternalServerErrorStatusCode_WhenNoUserTemplateGiven() throws Exception { + final String emptyString = ""; + final HttpServletResponse mockResponse = mock(HttpServletResponse.class); + final PrintWriter mockPrinter = mock(PrintWriter.class); + when(mockResponse.getWriter()).thenReturn(mockPrinter); + + final AnnotationHandler sut = spy(new AnnotationHandler()); + doThrow(IOException.class).when(sut).getResource(AnnotationHandler.USER_TEMPLATE); + doReturn(emptyString).when(sut).formatDefaultMessage(null); + + sut.handleException(TestExceptionWithNoContentStatusCodeAndTextContentType.class.getAnnotation(ExceptionHandler.class), null, mockResponse); + + verify(mockResponse).setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); + } + + @Test + public void handleException_ShouldRenderDefaultTemplate_WhenNoUserTemplateGiven() throws Exception { + final String expectedDefaultTemplate = "DEFAULT TEMPLATE: %s"; + final String expectedErrorMessage = "ERROR MESSAGE"; + final String expectedErrorBody = "DEFAULT TEMPLATE: ERROR MESSAGE"; + final TestExceptionWithNoContentStatusCodeAndTextContentType expectedException = new TestExceptionWithNoContentStatusCodeAndTextContentType(expectedErrorMessage); + final HttpServletResponse mockResponse = mock(HttpServletResponse.class); + final PrintWriter mockPrinter = mock(PrintWriter.class); + when(mockResponse.getWriter()).thenReturn(mockPrinter); + + final AnnotationHandler sut = spy(new AnnotationHandler()); + doThrow(IOException.class).when(sut).getResource(AnnotationHandler.USER_TEMPLATE); + doReturn(new ByteArrayInputStream(expectedDefaultTemplate.getBytes())).when(sut).getResource(AnnotationHandler.DEFAULT_TEMPLATE); + + sut.handleException(expectedException.getClass().getAnnotation(ExceptionHandler.class), expectedException, mockResponse); + + verify(mockPrinter).write(expectedErrorBody); + } + + @Test public void resolveException_ShouldReturnCustomErrorMessage_WhenValidExceptionWithAnnotationIsGiven() throws Exception { + final ExceptionHandler mockAnnotation = mock(ExceptionHandler.class); + final HttpServletResponse mockResponse = mock(HttpServletResponse.class); + final TestExceptionWithNoAnnotationAttributes expectedException = new TestExceptionWithNoAnnotationAttributes(""); + + final AnnotationHandler sut = spy(new AnnotationHandler()); + doReturn(new ModelAndView()).when(sut).handleException(mockAnnotation, expectedException, mockResponse); + doReturn(mockAnnotation).when(sut).getAnnotationFrom(expectedException); + + final ModelAndView view = sut.resolveException(null, mockResponse, null, expectedException); + + verify(sut).handleException(mockAnnotation, expectedException, mockResponse); + } } @ExceptionHandler() @@ -185,8 +250,17 @@ class TestExceptionWithNoAnnotationAttributes extends Exception { } @ExceptionHandler(contentType = MediaType.APPLICATION_XML_VALUE) -class TestExceptionWithXmlContentType extends Exception { } +class TestExceptionWithXmlContentType extends Exception { +} @ExceptionHandler(httpStatus = HttpStatus.NOT_FOUND) -class TestExceptionWithNotFoundStatusCode extends Exception { } +class TestExceptionWithNotFoundStatusCode extends Exception { +} + +@ExceptionHandler(contentType = MediaType.TEXT_PLAIN_VALUE, httpStatus = HttpStatus.NO_CONTENT) +class TestExceptionWithNoContentStatusCodeAndTextContentType extends Exception { + public TestExceptionWithNoContentStatusCodeAndTextContentType(final String s) { + super(s); + } +} diff --git a/sample/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml b/sample/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml index c75b2d8..2ec066d 100755 --- a/sample/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml +++ b/sample/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml @@ -20,6 +20,6 @@ - + \ No newline at end of file