ApiExceptionHandler.java
/*
* Copyright 2019 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gringlobal.api;
import java.io.EOFException;
import javax.persistence.EntityNotFoundException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.genesys.blocks.security.NoUserFoundException;
import org.genesys.blocks.security.NotUniqueUserException;
import org.genesys.blocks.security.service.PasswordPolicy;
import org.genesys.filerepository.FileRepositoryException;
import org.genesys.filerepository.NoSuchRepositoryFileException;
import org.genesys.filerepository.NoSuchRepositoryFolderException;
import org.gringlobal.api.exception.DetailedConstraintViolationException;
import org.gringlobal.api.exception.InvalidApiUsageException;
import org.gringlobal.api.exception.NotFoundElement;
import org.hibernate.hql.internal.ast.InvalidPathException;
import org.hibernate.hql.internal.ast.QuerySyntaxException;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
/**
* API exception handler returns errors in {@link ApiError}.
*
* @author Matija Obreza
*/
@ControllerAdvice(basePackages = { "org.gringlobal.api" })
@Slf4j
public class ApiExceptionHandler {
// @ResponseStatus(code = HttpStatus.NOT_FOUND)
// @ExceptionHandler(NoSuchAccessionException.class)
// @ResponseBody
// public ApiError<Exception> handleMissingAccession(NoSuchAccessionException
// ex, WebRequest request) throws JsonProcessingException {
// LOG.warn("Returning BrAPI error: " + ex.getMessage());
// return new ApiError<>(ex);
// }
@ExceptionHandler({ EOFException.class })
public void handleJettyEof(final HttpServletRequest request) {
log.warn("Client disconnected {} {}", request.getMethod(), request.getRequestURL());
}
/**
* Handle missing credentials.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
@ExceptionHandler({ AuthenticationCredentialsNotFoundException.class })
@ResponseBody
public ApiError<Exception> handleMissingCredentials(final Exception e, final WebRequest request) {
log.warn("Authentication is required.", e);
return new ApiError<>(e);
}
/**
* Handle access denied.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.FORBIDDEN)
@ExceptionHandler({ AccessDeniedException.class })
@ResponseBody
public ApiError<Exception> handleAccessDenied(final Exception e, final HttpServletRequest request) {
log.warn("Authentication is required {} {}", request.getMethod(), request.getRequestURL());
return new ApiError<>(e);
}
/**
* Handle converter error.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseBody
public ApiError<Exception> handleConverterError(final HttpMessageNotReadableException e, final HttpServletRequest request) {
log.warn("Invalid payload provided {} {}", request.getMethod(), request.getRequestURL(), e);
return new ApiError<>(e);
}
/**
* Handle javax validation error.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public ApiError<Exception> handleValidationError(final ConstraintViolationException e, final HttpServletRequest request) {
final DetailedConstraintViolationException exception = new DetailedConstraintViolationException(e);
log.warn("{} for {} {}", exception.getMessage(), request.getMethod(), request.getRequestURL(), e);
return new ApiError<>(exception);
}
/**
* Handle converter error.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
@ResponseBody
public ApiError<Exception> handleConverterArgumentError(final MethodArgumentTypeMismatchException e, final HttpServletRequest request) {
log.warn("Invalid argument {} for {} provided {} {}: {}", e.getName(), e.getParameter(), request.getMethod(), request.getRequestURL(), e.getMessage());
return new ApiError<>(e);
}
/**
* Handle missing request parameters.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseBody
public ApiError<Exception> handleConverterArgumentError(final MissingServletRequestParameterException e, final HttpServletRequest request) {
log.warn("Missing request parameter {} for {} {}: {}", e.getParameterName(), request.getMethod(), request.getRequestURL(), e.getMessage());
return new ApiError<>(e);
}
/**
* Handle password policy mismatch.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
@ExceptionHandler(PasswordPolicy.PasswordPolicyException.class)
@ResponseBody
public ApiError<Exception> handlePasswordPolicyMismatch(final PasswordPolicy.PasswordPolicyException e, final HttpServletRequest request) {
log.warn("{} for {} {}", e.getMessage(), request.getMethod(), request.getRequestURL());
return new ApiError<>(e);
}
/**
* Handle invalid api usage.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
@ExceptionHandler({ InvalidApiUsageException.class, DataIntegrityViolationException.class, ConcurrencyFailureException.class, NotUniqueUserException.class, InvalidPathException.class, QuerySyntaxException.class, FileRepositoryException.class, MethodArgumentNotValidException.class })
@ResponseBody
public ApiError<Exception> handleInvalidApiUsage(final Exception e, final HttpServletRequest request) {
log.warn("{} for {} {}", e.getMessage(), request.getMethod(), request.getRequestURL(), e);
return new ApiError<>(e);
}
/**
* Handle not found.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.NOT_FOUND)
@ExceptionHandler(value = { EntityNotFoundException.class, NotFoundElement.class, NoSuchRepositoryFileException.class, NoSuchRepositoryFolderException.class, NoUserFoundException.class })
@ResponseBody
public ApiError<Exception> handleNotFound(final Exception e, final HttpServletRequest request) {
log.warn("Element not found {} {}", request.getMethod(), request.getRequestURL());
return new ApiError<>(e);
}
/**
* Handle request method fail.
*
* @param req the req
* @param e the e
* @return the model and view
*/
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
@ExceptionHandler(value = { HttpRequestMethodNotSupportedException.class })
public ApiError<Exception> handleRequestMethodFail(final HttpServletRequest req, final HttpRequestMethodNotSupportedException e) {
log.warn("Request method {} not supported for URL {}", e.getMethod(), req.getRequestURL());
return new ApiError<>(e);
}
/**
* Handle server error.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
@ResponseBody
public ApiError<Exception> handleIllegalArgumentException(final Exception e, final WebRequest request) {
log.warn("Illegal argument: {}", e.getMessage());
return new ApiError<>(e);
}
/**
* Handle server error.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Throwable.class)
@ResponseBody
public ApiError<Exception> handleServerError(final Exception e, final WebRequest request) {
log.warn("Wow! Such! Exception!", e);
return new ApiError<>(e);
}
/**
* Handle server error.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(NullPointerException.class)
@ResponseBody
public ApiError<Exception> handleNPE(final Exception e, final WebRequest request) {
log.error("NPE in {}", request.getDescription(true), e);
return new ApiError<>(e);
}
}