diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/controller/FooController.java b/spring-boot-rest/src/main/java/com/baeldung/web/controller/FooController.java index 8174480078..a09878fb84 100644 --- a/spring-boot-rest/src/main/java/com/baeldung/web/controller/FooController.java +++ b/spring-boot-rest/src/main/java/com/baeldung/web/controller/FooController.java @@ -4,6 +4,8 @@ import java.util.List; import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Page; @@ -11,6 +13,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -25,6 +28,8 @@ import org.springframework.web.util.UriComponentsBuilder; import com.baeldung.persistence.model.Foo; import com.baeldung.persistence.service.IFooService; +import com.baeldung.web.exception.CustomException1; +import com.baeldung.web.exception.CustomException2; import com.baeldung.web.exception.MyResourceNotFoundException; import com.baeldung.web.hateoas.event.PaginatedResultsRetrievedEvent; import com.baeldung.web.hateoas.event.ResourceCreatedEvent; @@ -36,6 +41,8 @@ import com.google.common.base.Preconditions; @RequestMapping(value = "/foos") public class FooController { + private static final Logger logger = LoggerFactory.getLogger(FooController.class); + @Autowired private ApplicationEventPublisher eventPublisher; @@ -137,4 +144,10 @@ public class FooController { public void delete(@PathVariable("id") final Long id) { service.deleteById(id); } + + @ExceptionHandler({ CustomException1.class, CustomException2.class }) + public void handleException(final Exception ex) { + final String error = "Application specific error handling"; + logger.error(error, ex); + } } diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/error/RestResponseStatusExceptionResolver.java b/spring-boot-rest/src/main/java/com/baeldung/web/error/RestResponseStatusExceptionResolver.java new file mode 100644 index 0000000000..6753ab35cf --- /dev/null +++ b/spring-boot-rest/src/main/java/com/baeldung/web/error/RestResponseStatusExceptionResolver.java @@ -0,0 +1,73 @@ +package com.baeldung.web.error; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + +@Component +public class RestResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver { + + private static final Logger logger = LoggerFactory.getLogger(RestResponseStatusExceptionResolver.class); + + @Override + protected ModelAndView doResolveException(HttpServletRequest request, + HttpServletResponse response, Object handler, Exception ex) { + try { + if (ex instanceof IllegalArgumentException) { + return handleIllegalArgument( + (IllegalArgumentException) ex, request, response, handler); + } + } catch (Exception handlerException) { + logger.warn("Handling of [{}] resulted in Exception", ex.getClass().getName(), handlerException); + } + return null; + } + + private ModelAndView handleIllegalArgument(IllegalArgumentException ex, + final HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + final String accept = request.getHeader(HttpHeaders.ACCEPT); + + response.sendError(HttpServletResponse.SC_CONFLICT); + response.setHeader("ContentType", accept); + + final ModelAndView modelAndView = new ModelAndView("error"); + modelAndView.addObject("error", prepareErrorResponse(accept)); + return modelAndView; + } + + /** Prepares error object based on the provided accept type. + * @param accept The Accept header present in the request. + * @return The response to return + * @throws JsonProcessingException + */ + private String prepareErrorResponse(String accept) throws JsonProcessingException { + final Map error = new HashMap<>(); + error.put("Error", "Application specific error message"); + + final String response; + if(MediaType.APPLICATION_JSON_VALUE.equals(accept)) { + response = new ObjectMapper().writeValueAsString(error); + } else { + response = new XmlMapper().writeValueAsString(error); + } + + return response; + } + + + +} diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/exception/CustomException1.java b/spring-boot-rest/src/main/java/com/baeldung/web/exception/CustomException1.java new file mode 100644 index 0000000000..ed8d34ae2b --- /dev/null +++ b/spring-boot-rest/src/main/java/com/baeldung/web/exception/CustomException1.java @@ -0,0 +1,7 @@ +package com.baeldung.web.exception; + +public class CustomException1 extends RuntimeException { + + private static final long serialVersionUID = 1L; + +} diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/exception/CustomException2.java b/spring-boot-rest/src/main/java/com/baeldung/web/exception/CustomException2.java new file mode 100644 index 0000000000..39b6c98a82 --- /dev/null +++ b/spring-boot-rest/src/main/java/com/baeldung/web/exception/CustomException2.java @@ -0,0 +1,7 @@ +package com.baeldung.web.exception; + +public class CustomException2 extends RuntimeException { + + private static final long serialVersionUID = 1L; + +} diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyResourceNotFoundException.java b/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyResourceNotFoundException.java index fd002efc28..59bcfde57a 100644 --- a/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyResourceNotFoundException.java +++ b/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyResourceNotFoundException.java @@ -1,5 +1,9 @@ package com.baeldung.web.exception; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.NOT_FOUND) public final class MyResourceNotFoundException extends RuntimeException { public MyResourceNotFoundException() { diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java index bd98523b0a..4d4a274b1a 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java @@ -10,8 +10,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import java.util.Collections; import org.hamcrest.Matchers; +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -24,6 +26,7 @@ import org.springframework.test.web.servlet.MockMvc; import com.baeldung.persistence.model.Foo; import com.baeldung.persistence.service.IFooService; import com.baeldung.web.controller.FooController; +import com.baeldung.web.exception.CustomException1; import com.baeldung.web.hateoas.event.PaginatedResultsRetrievedEvent; /** @@ -56,5 +59,15 @@ public class FooControllerWebLayerIntegrationTest { .andExpect(status().isOk()) .andExpect(jsonPath("$",Matchers.hasSize(1))); } - + + @Test + public void delete_forException_fromService() throws Exception { + Mockito.when(service.findAll()).thenThrow(new CustomException1()); + this.mockMvc.perform(get("/foos")).andDo(h -> { + final Exception expectedException = h.getResolvedException(); + Assert.assertTrue(expectedException instanceof CustomException1); + + }); + } + }