From cf7cfeedc45a6bb35096a06f6f774e8229cc7301 Mon Sep 17 00:00:00 2001 From: raychatter Date: Wed, 5 Dec 2012 11:23:16 -0800 Subject: [PATCH 1/7] Updated README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 13c4453..f79e630 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ The defaults are `httpStatus = HttpStatus.INTERNAL_SERVER_ERROR` and `contentTyp The custom message is taken from the custom annotation class itself, so any parameters you'd like to insert need to be handled there. Make sure to add `com.raychatter.common.annotation.AnnotationHandler` as a bean in your XML. + And that's it! From 943b3f722e705768768f72f4f2f2007c3a02c530 Mon Sep 17 00:00:00 2001 From: raychatter Date: Wed, 5 Dec 2012 16:21:04 -0800 Subject: [PATCH 2/7] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f79e630..68d5fb9 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ The defaults are `httpStatus = HttpStatus.INTERNAL_SERVER_ERROR` and `contentTyp The custom message is taken from the custom annotation class itself, so any parameters you'd like to insert need to be handled there. -Make sure to add `com.raychatter.common.annotation.AnnotationHandler` as a bean in your XML. +Make sure to add `` to your XML. And that's it! From b49d8cc31cac77f4928fd61c13515b96234f3457 Mon Sep 17 00:00:00 2001 From: Isa Goksu Date: Fri, 7 Dec 2012 12:42:15 -0800 Subject: [PATCH 3/7] Added a default behavior for the exceptions that are not checked --- .../common/annotation/AnnotationHandler.java | 19 ++++++++++++++----- .../common/controller/HelloController.java | 6 ++++++ 2 files changed, 20 insertions(+), 5 deletions(-) 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 353b05f..12ce0ca 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 @@ -23,9 +23,12 @@ public class AnnotationHandler implements HandlerExceptionResolver { public ModelAndView resolveException(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception thrownException) { final ExceptionHandler annotation = getAnnotationFrom(thrownException); - if (annotation == null) return new ModelAndView(); - try { + if (annotation == null) { + thrownException.printStackTrace(); + return respondWithDefault(thrownException, response); + } + return handleException(annotation, thrownException, response); } catch (IOException e) { // potentially something went wrong in response itself @@ -43,14 +46,20 @@ public class AnnotationHandler implements HandlerExceptionResolver { final String message = formatMessage(thrownException); response.getWriter().write(message); } catch (IOException e) { - response.setContentType(MediaType.APPLICATION_XML_VALUE); - response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); - response.getWriter().write(formatDefaultMessage(thrownException)); + return respondWithDefault(thrownException, response); } return new ModelAndView(); } + private ModelAndView respondWithDefault(final Exception thrownException, final HttpServletResponse response) throws IOException { + 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); } diff --git a/sample/src/main/java/com/raychatter/common/controller/HelloController.java b/sample/src/main/java/com/raychatter/common/controller/HelloController.java index e3dfbe0..77369be 100755 --- a/sample/src/main/java/com/raychatter/common/controller/HelloController.java +++ b/sample/src/main/java/com/raychatter/common/controller/HelloController.java @@ -29,4 +29,10 @@ public class HelloController { throw new MyNegativeArraySizeException("oops"); } + @RequestMapping(value = "/unchecked", method = RequestMethod.GET) + @ResponseBody + public Object unchecked() throws Exception { + throw new NullPointerException("an npe"); + } + } From afc12ba2ae3c1d30906ae722a3fbbc3d00fa5e4c Mon Sep 17 00:00:00 2001 From: Isa Goksu Date: Fri, 7 Dec 2012 12:48:57 -0800 Subject: [PATCH 4/7] Forgot to add the tests --- .../common/annotation/AnnotationHandler.java | 2 +- .../common/annotation/AnnotationHandlerTest.java | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) 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 12ce0ca..14d9415 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 @@ -52,7 +52,7 @@ public class AnnotationHandler implements HandlerExceptionResolver { return new ModelAndView(); } - private ModelAndView respondWithDefault(final Exception thrownException, final HttpServletResponse response) throws IOException { + protected ModelAndView respondWithDefault(final Exception thrownException, final HttpServletResponse response) throws IOException { response.setContentType(MediaType.APPLICATION_XML_VALUE); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.getWriter().write(formatDefaultMessage(thrownException)); 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 83e35f4..2675cd9 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 @@ -240,6 +240,21 @@ public class AnnotationHandlerTest { verify(sut).handleException(mockAnnotation, expectedException, mockResponse); } + + @Test public void resolveException_ShouldReturnDefaultErrorMessage_WhenUncheckedExceptionIsGiven() throws Exception { + final NullPointerException expectedException = mock(NullPointerException.class); + + final HttpServletResponse mockResponse = mock(HttpServletResponse.class); + final PrintWriter mockPrinter = mock(PrintWriter.class); + when(mockResponse.getWriter()).thenReturn(mockPrinter); + + final AnnotationHandler sut = spy(new AnnotationHandler()); + doReturn(null).when(sut).getAnnotationFrom(expectedException); + + final ModelAndView view = sut.resolveException(null, mockResponse, null, expectedException); + + verify(sut).respondWithDefault(expectedException, mockResponse); + } } @ExceptionHandler() From c31ecc6b2732ce0ee899554b169d666b2830e9c5 Mon Sep 17 00:00:00 2001 From: Isa Goksu Date: Fri, 7 Dec 2012 12:58:09 -0800 Subject: [PATCH 5/7] Enhanced the readme file a bit --- README.md | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 68d5fb9..0c73486 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,49 @@ # spring-restful-exception-handler -### An annotation for the Spring framework to handle HTTP responses for custom exceptions. + +An annotation for the Spring framework to handle HTTP responses for custom exceptions. ## How to use spring-restful-exception-handler + Under PROJECT/resources create a file called *error.template*. Inside this file, place the formatted error template you want to return when a custom exception is thrown. If no custom template is given, the following default template will be used: -``` %s ``` +```xml + + %s +``` -Annotate the custom exception class with `@Exception(*httpStatus*, *contentType*)`. -The defaults are `httpStatus = HttpStatus.INTERNAL_SERVER_ERROR` and `contentType = MediaType.APPLICATION_XML_VALUE`. +Annotate the custom exception class with `@ExceptionHandler(*httpStatus*, *contentType*)`. The defaults are `httpStatus = HttpStatus.INTERNAL_SERVER_ERROR` and `contentType = MediaType.APPLICATION_XML_VALUE`. So an example exception class would be: + +```java +@ExceptionHandler(httpStatus = HttpStatus.NOT_FOUND, contentType = MediaType.APPLICATION_XML_VALUE) +public class MyCustomException extends Exception { + public MyCustomException(final String message) { + super(message); + } +} +``` The custom message is taken from the custom annotation class itself, so any parameters you'd like to insert need to be handled there. -Make sure to add `` to your XML. +Make sure to add following to your servlet XML: -And that's it! +```xml + +``` +And that's it! Just keep in mind that the exception handler will take care of all the exceptions and by default it will return `Internal Server Error` with an `XML` body described above. If you want to override, make sure you have `error.template` in your classpath with `%s` for the message placeholder. An example for a `error.template` file in your classpath for a json response: +```json +{ + "message": "%s" +} + +And now you need to make sure that your exceptions are returning `json` content type. + +```java +@ExceptionHandler(httpStatus = HttpStatus.CONFLICT, contentType = MediaType.APPLICATION_JSON_VALUE) +public class MyCustomException extends Exception { + public MyCustomException(final String message) { + super(message); + } +} +``` From a556fd6ab776c19af299ade4f807378457822622 Mon Sep 17 00:00:00 2001 From: Isa Goksu Date: Fri, 7 Dec 2012 12:58:48 -0800 Subject: [PATCH 6/7] Forgot to close code block --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0c73486..ad3d285 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ And that's it! Just keep in mind that the exception handler will take care of al { "message": "%s" } +``` And now you need to make sure that your exceptions are returning `json` content type. From 4fd8cca9d5ff07dfc14ae0eb862e292714076ba5 Mon Sep 17 00:00:00 2001 From: Isa Goksu Date: Fri, 7 Dec 2012 13:04:52 -0800 Subject: [PATCH 7/7] Added couple more descriptive things to the readme --- README.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ad3d285..36a0817 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,12 @@ # spring-restful-exception-handler -An annotation for the Spring framework to handle HTTP responses for custom exceptions. - -## How to use spring-restful-exception-handler - -Under PROJECT/resources create a file called *error.template*. Inside this file, place the formatted error template you want to return when a custom exception is thrown. If no custom template is given, the following default template will be used: +An annotation for the Spring framework to handle HTTP responses for custom exceptions. The way it works is you have to change the default exception resolver for Spring to our custom exception resolver by modifying your servlet xml file for Spring: ```xml - - %s + ``` -Annotate the custom exception class with `@ExceptionHandler(*httpStatus*, *contentType*)`. The defaults are `httpStatus = HttpStatus.INTERNAL_SERVER_ERROR` and `contentType = MediaType.APPLICATION_XML_VALUE`. So an example exception class would be: +After overriding the exception resolving mechanism, just annotate the custom exception classes with `@ExceptionHandler(*httpStatus*, *contentType*)`. The defaults are `httpStatus = HttpStatus.INTERNAL_SERVER_ERROR` and `contentType = MediaType.APPLICATION_XML_VALUE`. So an example exception class would be: ```java @ExceptionHandler(httpStatus = HttpStatus.NOT_FOUND, contentType = MediaType.APPLICATION_XML_VALUE) @@ -24,13 +19,16 @@ public class MyCustomException extends Exception { The custom message is taken from the custom annotation class itself, so any parameters you'd like to insert need to be handled there. -Make sure to add following to your servlet XML: +And that's it! Just keep in mind that the exception handler will take care of all the exceptions and by default it will return `Internal Server Error` with an `XML` body described above. If you don't specify any custom template for your error responses, following error template will be used by default: ```xml - + + + %s + ``` -And that's it! Just keep in mind that the exception handler will take care of all the exceptions and by default it will return `Internal Server Error` with an `XML` body described above. If you want to override, make sure you have `error.template` in your classpath with `%s` for the message placeholder. An example for a `error.template` file in your classpath for a json response: +If you want to override, make sure you have `error.template` in your classpath with `%s` for the message placeholder. An example for a `error.template` file in your classpath for a json response: ```json {