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 3591642..abd2bf9 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; @@ -13,45 +15,64 @@ import java.util.Scanner; public class AnnotationHandler implements HandlerExceptionResolver { @Override - public ModelAndView resolveException(final HttpServletRequest request, - final HttpServletResponse response, - final Object handler, - final Exception thrownException) { + 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 exceptionHandlerAnnotation = thrownException.getClass().getAnnotation(ExceptionHandler.class); - if (exceptionHandlerAnnotation == null) { +// This still returns an empty ModelAndView + if (annotation == null) { return new ModelAndView(); } - return doStuffWithAnnotation(exceptionHandlerAnnotation, thrownException, response); + return doStuffWithAnnotation(annotation, thrownException, response); } private ModelAndView doStuffWithAnnotation(final ExceptionHandler exceptionHandlerAnnotation, final Exception thrownException, final HttpServletResponse response) { +// This is only outside of the try because the null annotation case is handled in resolveException response.setContentType(exceptionHandlerAnnotation.contentType()); response.setStatus(exceptionHandlerAnnotation.httpStatus().value()); + try { response.getWriter().write(formatMessage(thrownException)); } catch (IOException e) { - e.printStackTrace(); + response.setContentType(MediaType.APPLICATION_XML_VALUE); + response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); + + try { + response.getWriter().write(formatDefaultMessage(thrownException)); + } catch (IOException ex) { + ex.printStackTrace(); + } } + return new ModelAndView(); } -// Customize your output message here. If you have multiple dynamic parameters to -// put into your template, you can assign them all in this method. - private String formatMessage(final Exception thrownException) { + protected String formatMessage(final Exception thrownException) throws IOException { return String.format(readTemplate(), thrownException.getMessage()); } - // Reads the template file until the end of the line - private String readTemplate() { + protected String formatDefaultMessage(final Exception thrownException) throws IOException { + return String.format(readDefaultTemplate(), thrownException.getMessage()); + } + + protected String readTemplate() throws IOException { + final InputStream templateFile = new ClassPathResource("error.template").getInputStream(); + return new Scanner(templateFile, "UTF-8").useDelimiter("\\A").next().trim(); + } + +// Extract into a new protected method to grab input stream; spy that new method, returning null? + protected String readDefaultTemplate() { try { - final InputStream templateFile = new ClassPathResource("error.template").getInputStream(); - return new Scanner(templateFile, "UTF-8").useDelimiter("\\A").next().trim(); - } catch (IOException exception) { - return "Default Error Message: %s"; + final InputStream templateFile = getInputStream("defaults/default.template"); + return new Scanner(templateFile, "UTF-8").useDelimiter("\\A").next().trim(); + } catch (IOException ex) { + return "Error: %s"; } } + protected InputStream getInputStream(String filepath) throws IOException { + return new ClassPathResource(filepath).getInputStream(); + } + } diff --git a/exception-handler/src/main/resources/defaults/default.template b/exception-handler/src/main/resources/defaults/default.template new file mode 100644 index 0000000..1e3c45a --- /dev/null +++ b/exception-handler/src/main/resources/defaults/default.template @@ -0,0 +1,4 @@ + + +%s + \ No newline at end of file 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 532dd67..ad0f0c9 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 @@ -1,13 +1,52 @@ package com.raychatter.common.annotation; -import junit.framework.TestCase; +import junit.framework.Assert; import org.junit.Test; -public class AnnotationHandlerTest extends TestCase { +import java.io.IOException; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.spy; + +public class AnnotationHandlerTest { @Test - public void shouldReturnModelAndViewIfAnnotationIsNull() { - AnnotationHandler annotationHandler = new AnnotationHandler(); + public void formatMessage_ShouldRenderUserTemplate_WhenUserTemplateGiven() throws Exception { + final String givenUserTemplate = "A TEMPLATE: %s"; + final String exceptionMessage = "AN ERROR MESSAGE"; + final String expectedResult = "A TEMPLATE: AN ERROR MESSAGE"; + + AnnotationHandler sut = spy(new AnnotationHandler()); + doReturn(givenUserTemplate).when(sut).readTemplate(); + + final String actual = sut.formatMessage(new Exception(exceptionMessage)); + + Assert.assertEquals(expectedResult, actual); } -} + @Test public void formatDefaultMessage_ShouldRenderDefaultUserTemplate_WhenNoUserTemplateGiven() throws Exception { + final String defaultTemplate = "DEFAULT TEMPLATE: %s"; + final String exceptionMessage = "AN ERROR MESSAGE"; + final String expectedMessage = "DEFAULT TEMPLATE: AN ERROR MESSAGE"; + + AnnotationHandler spyAnnotationHandler = spy(new AnnotationHandler()); + doReturn(defaultTemplate).when(spyAnnotationHandler).readDefaultTemplate(); + + final String actual = spyAnnotationHandler.formatDefaultMessage(new Exception(exceptionMessage)); + + Assert.assertEquals(expectedMessage, actual); + } + + @Test public void readDefaultTemplate_ShouldCatchIOException_WhenDefaultTemplateNotFound() throws Exception { + final String expectedTemplate = "Error: %s"; + final String defaultFilepath = "defaults/default.template"; + + AnnotationHandler spyAnnotationHandler = spy(new AnnotationHandler()); + doThrow(new IOException()).when(spyAnnotationHandler).getInputStream(defaultFilepath); + + final String actualTemplate = spyAnnotationHandler.readDefaultTemplate(); + + Assert.assertEquals(expectedTemplate, actualTemplate); + } +} \ No newline at end of file