diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestConfiguration.java index 4debccc11..c0c3f082b 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestConfiguration.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestConfiguration.java @@ -22,7 +22,7 @@ public class RepositoryRestConfiguration { private String jsonpOnErrParamName = null; private List> customConverters = Collections.emptyList(); private MediaType defaultMediaType = MediaType.APPLICATION_JSON; - private boolean dumpErrors = false; + private boolean dumpErrors = true; public int getDefaultPageSize() { return defaultPageSize; diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java index 0cc623934..365d06cf2 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java @@ -31,7 +31,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.convert.ConversionService; -import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -1367,38 +1366,8 @@ public class RepositoryRestController @ResponseBody public ResponseEntity handleNPE(NullPointerException e, ServletServerHttpRequest request) throws IOException { - if(LOG.isErrorEnabled()) { - LOG.error(e.getMessage(), e); - } - return negotiateResponse(request, HttpStatus.INTERNAL_SERVER_ERROR, new HttpHeaders(), null); - } - - /** - * Handle {@link InvocationTargetException}s as a 400 Bad Request because these are likely to occur if, e.g. the user - * does not provide a value for a query parameter. - * - * @param e - * @param request - * - * @return - * - * @throws IOException - */ - @ExceptionHandler(InvocationTargetException.class) - @ResponseBody - public ResponseEntity handleInvocationTargetException(InvocationTargetException e, - ServletServerHttpRequest request) throws IOException { - if(LOG.isErrorEnabled()) { - LOG.error(e.getMessage(), e); - } - - for(Throwable cause = e.getCause(); (null != cause && cause != e.getCause()); cause = cause.getCause()) { - if(cause instanceof InvalidDataAccessApiUsageException || cause instanceof IllegalArgumentException) { - return negotiateResponse(request, HttpStatus.BAD_REQUEST, new HttpHeaders(), null); - } - } - - return negotiateResponse(request, HttpStatus.INTERNAL_SERVER_ERROR, new HttpHeaders(), e); + LOG.error(e.getMessage(), e); + return errorResponse(request, HttpStatus.INTERNAL_SERVER_ERROR, e); } /** @@ -1413,6 +1382,7 @@ public class RepositoryRestController */ @ExceptionHandler( { + InvocationTargetException.class, IllegalArgumentException.class, ClassCastException.class } @@ -1420,10 +1390,8 @@ public class RepositoryRestController @ResponseBody public ResponseEntity handleMiscFailures(Throwable t, ServletServerHttpRequest request) throws IOException { - if(LOG.isErrorEnabled()) { - LOG.error(t.getMessage(), t); - } - return negotiateResponse(request, HttpStatus.BAD_REQUEST, new HttpHeaders(), t); + LOG.error(t.getMessage(), t); + return errorResponse(request, HttpStatus.BAD_REQUEST, t); } /** @@ -1442,11 +1410,7 @@ public class RepositoryRestController public ResponseEntity handleLockingFailure(OptimisticLockingFailureException ex, ServletServerHttpRequest request) throws IOException { LOG.error(ex.getMessage(), ex); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - Map m = new HashMap(); - m.put("message", ex.getMessage()); - return negotiateResponse(request, HttpStatus.CONFLICT, headers, objectMapper.writeValueAsBytes(m)); + return errorResponse(request, HttpStatus.CONFLICT, ex); } /** @@ -1493,13 +1457,11 @@ public class RepositoryRestController ServletServerHttpRequest request) throws IOException { LOG.error(ex.getMessage(), ex); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); Map m = new HashMap(); m.put("message", ex.getMessage()); m.put("acceptableTypes", availableMediaTypes); - return negotiateResponse(request, HttpStatus.BAD_REQUEST, headers, objectMapper.writeValueAsBytes(m)); + return negotiateResponse(request, HttpStatus.BAD_REQUEST, new HttpHeaders(), m); } /* @@ -1657,10 +1619,30 @@ public class RepositoryRestController && !config.getSortParamName().equals(name)); } + @SuppressWarnings({"unchecked"}) + private Map throwableToMap(Throwable t) { + Map m = new HashMap(); + m.put("message", t.getMessage()); + if(null != t.getCause()) { + m.put("cause", throwableToMap(t.getCause())); + } + return m; + } + private ResponseEntity notFoundResponse(ServletServerHttpRequest request) throws IOException { return negotiateResponse(request, HttpStatus.NOT_FOUND, new HttpHeaders(), null); } + @SuppressWarnings({"unchecked"}) + private ResponseEntity errorResponse(ServletServerHttpRequest request, HttpStatus status, Throwable t) + throws IOException { + Object body = null; + if(config.isDumpErrors()) { + body = throwableToMap(t); + } + return negotiateResponse(request, status, new HttpHeaders(), body); + } + @SuppressWarnings({"unchecked"}) private ResponseEntity negotiateResponse(final ServletServerHttpRequest request, final HttpStatus status, diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestMvcConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestMvcConfiguration.java index dda706110..1bf20da4f 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestMvcConfiguration.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestMvcConfiguration.java @@ -1,5 +1,7 @@ package org.springframework.data.rest.webmvc; +import java.util.Arrays; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -7,7 +9,8 @@ import org.springframework.context.annotation.ImportResource; import org.springframework.data.rest.repository.context.ValidatingRepositoryEventListener; import org.springframework.data.rest.repository.jpa.JpaRepositoryExporter; import org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor; -import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver; /** * @author Jon Brisbin @@ -58,4 +61,12 @@ public class RepositoryRestMvcConfiguration { return new RepositoryRestHandlerMapping(); } + @Bean public ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver() { + ExceptionHandlerExceptionResolver er = new ExceptionHandlerExceptionResolver(); + er.setCustomArgumentResolvers( + Arrays.asList(new ServerHttpRequestMethodArgumentResolver()) + ); + return er; + } + } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ThrowableHttpMessageConverter.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ThrowableHttpMessageConverter.java deleted file mode 100644 index c63e7f553..000000000 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ThrowableHttpMessageConverter.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.springframework.data.rest.webmvc; - -import java.io.IOException; -import java.io.PrintWriter; - -import org.codehaus.jackson.map.ObjectMapper; -import org.springframework.http.HttpInputMessage; -import org.springframework.http.HttpOutputMessage; -import org.springframework.http.MediaType; -import org.springframework.http.converter.AbstractHttpMessageConverter; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.http.converter.HttpMessageNotWritableException; - -/** - * @author Jon Brisbin - */ -public class ThrowableHttpMessageConverter extends AbstractHttpMessageConverter { - - private final ObjectMapper mapper = new ObjectMapper(); - - @Override protected boolean supports(Class clazz) { - throw new IllegalStateException("supports(Class clazz) not used in " + getClass().getName()); - } - - @Override public boolean canRead(Class clazz, MediaType mediaType) { - return false; - } - - @Override public boolean canWrite(Class clazz, MediaType mediaType) { - return (Throwable.class.isAssignableFrom(clazz) - && (mediaType.getSubtype().contains("json") || mediaType.getSubtype().contains("text"))); - } - - @Override protected Throwable readInternal(Class clazz, HttpInputMessage inputMessage) - throws IOException, HttpMessageNotReadableException { - throw new HttpMessageNotReadableException("Cannot read Throwables from input."); - } - - @Override protected void writeInternal(Throwable throwable, HttpOutputMessage outputMessage) - throws IOException, HttpMessageNotWritableException { - if(outputMessage.getHeaders().getContentType().getSubtype().contains("json")) { - outputMessage.getBody().write(mapper.writeValueAsBytes(throwable)); - } else { - throwable.printStackTrace(new PrintWriter(outputMessage.getBody())); - } - } - -}