Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,53 +15,33 @@
*/
package org.springframework.data.rest.webmvc;

import static org.springframework.data.rest.webmvc.ControllerUtils.*;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.rest.core.RepositoryConstraintViolationException;
import org.springframework.data.rest.core.mapping.ResourceMetadata;
import org.springframework.data.rest.webmvc.support.ETagDoesntMatchException;
import org.springframework.data.rest.webmvc.support.ExceptionMessage;
import org.springframework.data.rest.webmvc.support.RepositoryConstraintViolationExceptionMessage;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.Assert;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static org.springframework.data.rest.webmvc.ControllerUtils.EMPTY_RESOURCE_LIST;

/**
* @author Jon Brisbin
* @author Oliver Gierke
* @author Thibaud Lepretre
*/
@SuppressWarnings({ "rawtypes" })
class AbstractRepositoryRestController implements MessageSourceAware {
class AbstractRepositoryRestController {

private static final Logger LOG = LoggerFactory.getLogger(AbstractRepositoryRestController.class);

private final PagedResourcesAssembler<Object> pagedResourcesAssembler;
private MessageSourceAccessor messageSourceAccessor;

/**
* Creates a new {@link AbstractRepositoryRestController} for the given {@link PagedResourcesAssembler}.
Expand All @@ -74,129 +54,6 @@ public AbstractRepositoryRestController(PagedResourcesAssembler<Object> pagedRes
this.pagedResourcesAssembler = pagedResourcesAssembler;
}

/*
* (non-Javadoc)
* @see org.springframework.context.MessageSourceAware#setMessageSource(org.springframework.context.MessageSource)
*/
@Override
public void setMessageSource(MessageSource messageSource) {
this.messageSourceAccessor = new MessageSourceAccessor(messageSource);
}

@ExceptionHandler({ NullPointerException.class })
@ResponseBody
public ResponseEntity<?> handleNPE(NullPointerException npe) {
return errorResponse(npe, HttpStatus.INTERNAL_SERVER_ERROR);
}

@ExceptionHandler({ ResourceNotFoundException.class })
@ResponseBody
public ResponseEntity<?> handleNotFound() {
return notFound();
}

@ExceptionHandler({ HttpMessageNotReadableException.class })
@ResponseBody
public ResponseEntity<ExceptionMessage> handleNotReadable(HttpMessageNotReadableException e) {
return badRequest(e);
}

/**
* Handle failures commonly thrown from code tries to read incoming data and convert or cast it to the right type.
*
* @param t
* @return
*/
@ExceptionHandler({ InvocationTargetException.class, IllegalArgumentException.class, ClassCastException.class,
ConversionFailedException.class })
@ResponseBody
public ResponseEntity handleMiscFailures(Throwable t) {
if (null != t.getCause() && t.getCause() instanceof ResourceNotFoundException) {
return notFound();
}
return badRequest(t);
}

@ExceptionHandler({ RepositoryConstraintViolationException.class })
@ResponseBody
public ResponseEntity handleRepositoryConstraintViolationException(Locale locale,
RepositoryConstraintViolationException rcve) {

return response(null, new RepositoryConstraintViolationExceptionMessage(rcve, messageSourceAccessor),
HttpStatus.BAD_REQUEST);
}

/**
* Send a 409 Conflict in case of concurrent modification.
*
* @param ex
* @return
*/
@ExceptionHandler({ OptimisticLockingFailureException.class, DataIntegrityViolationException.class })
public ResponseEntity handleConflict(Exception ex) {
return errorResponse(null, ex, HttpStatus.CONFLICT);
}

/**
* Send {@code 405 Method Not Allowed} and include the supported {@link HttpMethod}s in the {@code Allow} header.
*
* @param o_O
* @return
*/
@ExceptionHandler
public ResponseEntity<Void> handle(HttpRequestMethodNotSupportedException o_O) {

HttpHeaders headers = new HttpHeaders();
headers.setAllow(o_O.getSupportedHttpMethods());

return new ResponseEntity<Void>(headers, HttpStatus.METHOD_NOT_ALLOWED);
}

@ExceptionHandler
public ResponseEntity<Void> handle(ETagDoesntMatchException o_O) {

HttpHeaders headers = o_O.getExpectedETag().addTo(new HttpHeaders());
return new ResponseEntity<Void>(headers, HttpStatus.PRECONDITION_FAILED);
}

protected <T> ResponseEntity<T> notFound() {
return notFound(null, null);
}

protected <T> ResponseEntity<T> notFound(HttpHeaders headers, T body) {
return response(headers, body, HttpStatus.NOT_FOUND);
}

protected <T extends Throwable> ResponseEntity<ExceptionMessage> badRequest(T throwable) {
return badRequest(null, throwable);
}

protected <T extends Throwable> ResponseEntity<ExceptionMessage> badRequest(HttpHeaders headers, T throwable) {
return errorResponse(headers, throwable, HttpStatus.BAD_REQUEST);
}

public <T extends Throwable> ResponseEntity<ExceptionMessage> errorResponse(T throwable, HttpStatus status) {
return errorResponse(null, throwable, status);
}

public <T extends Throwable> ResponseEntity<ExceptionMessage> errorResponse(HttpHeaders headers, T throwable,
HttpStatus status) {
if (null != throwable && null != throwable.getMessage()) {
LOG.error(throwable.getMessage(), throwable);
return response(headers, new ExceptionMessage(throwable), status);
} else {
return response(headers, null, status);
}
}

public <T> ResponseEntity<T> response(HttpHeaders headers, T body, HttpStatus status) {
HttpHeaders hdrs = new HttpHeaders();
if (null != headers) {
hdrs.putAll(headers);
}
return new ResponseEntity<T>(body, hdrs, status);
}

protected Link resourceLink(RootResourceInformation resourceLink, Resource resource) {

ResourceMetadata repoMapping = resourceLink.getResourceMetadata();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package org.springframework.data.rest.webmvc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.rest.core.RepositoryConstraintViolationException;
import org.springframework.data.rest.webmvc.support.ETagDoesntMatchException;
import org.springframework.data.rest.webmvc.support.ExceptionMessage;
import org.springframework.data.rest.webmvc.support.RepositoryConstraintViolationExceptionMessage;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.lang.reflect.InvocationTargetException;
import java.util.Locale;

/**
* @author Thibaud Lepretre
*/
@ControllerAdvice
public class GlobalExceptionHandler implements MessageSourceAware {

private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);


private MessageSourceAccessor messageSourceAccessor;

/*
* (non-Javadoc)
* @see org.springframework.context.MessageSourceAware#setMessageSource(org.springframework.context.MessageSource)
*/
@Override
public void setMessageSource(MessageSource messageSource) {
this.messageSourceAccessor = new MessageSourceAccessor(messageSource);
}

@ExceptionHandler({ NullPointerException.class })
@ResponseBody
public ResponseEntity<?> handleNPE(NullPointerException npe) {
return errorResponse(npe, HttpStatus.INTERNAL_SERVER_ERROR);
}

@ExceptionHandler({ ResourceNotFoundException.class })
@ResponseBody
public ResponseEntity<?> handleNotFound() {
return notFound();
}

@ExceptionHandler({ HttpMessageNotReadableException.class })
@ResponseBody
public ResponseEntity<ExceptionMessage> handleNotReadable(HttpMessageNotReadableException e) {
return badRequest(e);
}

/**
* Handle failures commonly thrown from code tries to read incoming data and convert or cast it to the right type.
*
* @param t
* @return
*/
@ExceptionHandler({ InvocationTargetException.class, IllegalArgumentException.class, ClassCastException.class,
ConversionFailedException.class })
@ResponseBody
public ResponseEntity handleMiscFailures(Throwable t) {
if (null != t.getCause() && t.getCause() instanceof ResourceNotFoundException) {
return notFound();
}
return badRequest(t);
}

@ExceptionHandler({ RepositoryConstraintViolationException.class })
@ResponseBody
public ResponseEntity handleRepositoryConstraintViolationException(Locale locale,
RepositoryConstraintViolationException rcve) {

return response(null, new RepositoryConstraintViolationExceptionMessage(rcve, messageSourceAccessor),
HttpStatus.BAD_REQUEST);
}

/**
* Send a 409 Conflict in case of concurrent modification.
*
* @param ex
* @return HTTP Status 409 ResponseEntity
*/
@ExceptionHandler({ OptimisticLockingFailureException.class, DataIntegrityViolationException.class })
public ResponseEntity handleConflict(Exception ex) {
return errorResponse(null, ex, HttpStatus.CONFLICT);
}

/**
* Send {@code 405 Method Not Allowed} and include the supported {@link org.springframework.http.HttpMethod}s in the {@code Allow} header.
*
* @param o_O
* @return HTTP Status 405 ResponseEntity
*/
@ExceptionHandler
public ResponseEntity<Void> handle(HttpRequestMethodNotSupportedException o_O) {

HttpHeaders headers = new HttpHeaders();
headers.setAllow(o_O.getSupportedHttpMethods());

return new ResponseEntity<Void>(headers, HttpStatus.METHOD_NOT_ALLOWED);
}

@ExceptionHandler
public ResponseEntity<Void> handle(ETagDoesntMatchException o_O) {

HttpHeaders headers = o_O.getExpectedETag().addTo(new HttpHeaders());
return new ResponseEntity<Void>(headers, HttpStatus.PRECONDITION_FAILED);
}

protected <T> ResponseEntity<T> notFound() {
return notFound(null, null);
}

protected <T> ResponseEntity<T> notFound(HttpHeaders headers, T body) {
return response(headers, body, HttpStatus.NOT_FOUND);
}

protected <T extends Throwable> ResponseEntity<ExceptionMessage> badRequest(T throwable) {
return badRequest(null, throwable);
}

protected <T extends Throwable> ResponseEntity<ExceptionMessage> badRequest(HttpHeaders headers, T throwable) {
return errorResponse(headers, throwable, HttpStatus.BAD_REQUEST);
}

public <T extends Throwable> ResponseEntity<ExceptionMessage> errorResponse(T throwable, HttpStatus status) {
return errorResponse(null, throwable, status);
}

public <T extends Throwable> ResponseEntity<ExceptionMessage> errorResponse(HttpHeaders headers, T throwable,
HttpStatus status) {
if (null != throwable && null != throwable.getMessage()) {
LOG.error(throwable.getMessage(), throwable);
return response(headers, new ExceptionMessage(throwable), status);
} else {
return response(headers, null, status);
}
}

public <T> ResponseEntity<T> response(HttpHeaders headers, T body, HttpStatus status) {
HttpHeaders hdrs = new HttpHeaders();
if (null != headers) {
hdrs.putAll(headers);
}
return new ResponseEntity<T>(body, hdrs, status);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import org.springframework.data.rest.webmvc.BaseUri;
import org.springframework.data.rest.webmvc.BaseUriAwareController;
import org.springframework.data.rest.webmvc.BaseUriAwareHandlerMapping;
import org.springframework.data.rest.webmvc.GlobalExceptionHandler;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.data.rest.webmvc.RepositoryRestHandlerAdapter;
import org.springframework.data.rest.webmvc.RepositoryRestHandlerMapping;
Expand Down Expand Up @@ -547,6 +548,11 @@ public ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver() {
return er;
}

@Bean
public GlobalExceptionHandler globalExceptionHandler() {
return new GlobalExceptionHandler();
}

@Bean
public RepositoryInvokerFactory repositoryInvokerFactory() {
return new DefaultRepositoryInvokerFactory(repositories(), defaultConversionService());
Expand Down
Loading