I have around 20 to 30 different types of different Exceptions all extending Exception class in Java.
one example is:
public class NoHandlerFoundException extends Exception {
private static final long serialVersionUID = -9079454849611061074L;
public NoHandlerFoundException() {
super();
}
public NoHandlerFoundException(final String message) {
super(message);
}
}
Other example is:
public class ResourceNotFoundException extends Exception{
private static final long serialVersionUID = -9079454849611061074L;
public ResourceNotFoundException() {
super();
}
public ResourceNotFoundException(final String message) {
super(message);
}
}
and many more.
As you can see most of the code is repeated and then I use ControllerAdvice like (I know code in ControllerAdvice argument exception class should be proper):
#ExceptionHandler({NoHandlerFoundException.class, ResourceNotFoundException.class})
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public #ResponseBody ExceptionResponse handleResourceNotFound(final NoHandlerFoundException exception,
final HttpServletRequest request) {
ExceptionResponse error = new ExceptionResponse();
error.setErrorMessage(exception.getMessage());
error.callerURL(request.getRequestURI());
return error;
}
So I want to know if we have any way in which I can optimize above exceptions and not write individual classes doing almost same job n times of times but still want to differentiate between them.
Thank you.
You can use below approach to reduce code redundancy.
#ExceptionHandler(value = {NoHandlerFoundException.class, ResourceNotFoundException.class})
protected ResponseEntity handleInvalidDataException(RuntimeException exception, WebRequest request) {
ExceptionResponse error = new ExceptionResponse();
error.setErrorMessage(exception.getMessage());
error.callerURL(request.getRequestURI());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
Related
I am trying to catch exceptions generated while executing some methods in a list.
I have created a different POJO class extending throwable class.
public class ErrorDetails extends Throwable implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private Exception errorDescription;
public Exception getErrorDescription() {
return errorDescription;
}
public void setErrorDescription(Exception errorDescription) {
this.errorDescription = errorDescription;
}
But still I cannot capture the exception in this manner.
private List<ErrorDetails> hello=new ArrayList<ErrorDetails>();
catch (Exception e) {
hello.add(e);
ErrorDetails is one specific type of Throwable. If you declare a list of ErrorDetails you can't add exceptions or errors in it. Change your code to use a list of Exception instead:
List<Exception> hello = new ...
I don't see a point to have your ErrorDetails class but in case you want to keep it, remove "extends Throwable" or replace it with something else. Throwable is meant to be a parent class only for Exception and Error.
I’m trying write a unit test for an ExceptionHandler class but I’m not 100% sure how to go about it. I’m struggling to work out how do I go about setting up the MethodArgumentNotValidException class with an error list?
#ControllerAdvice
public class InputExceptionHandler extends ResponseEntityExceptionHandler {
private static final String INVALID_CHARACTER_MESSAGE = "Fields contain invalid characters:";
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
final List<String> errorList = getErrorList(ex);
final StringBuilder errorMessage = new StringBuilder(popFirstError(errorList));
if(errorMessage.toString().contains(INVALID_CHARACTER_MESSAGE)) {
combineDuplicateErrorMessage(errorList, errorMessage);
}
return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(makeBankDetailsValidationModel(errorMessage.toString()));
}
private BankDetailsValidationModel makeBankDetailsValidationModel(String validationErrorDescription){
return new BankDetailsValidationModel(false, false, validationErrorDescription, "");
}
private StringBuilder combineDuplicateErrorMessage(List<String> errorList, StringBuilder errorMessage) {
return errorList.stream()
.filter(value -> value.contains(INVALID_CHARACTER_MESSAGE))
.findFirst()
.map(value -> errorMessage.append(", ").append(value.substring(35)))
.orElse(null);
}
private String popFirstError(List<String> errorMessages) {
final String message = errorMessages.get(0);
errorMessages.remove(0);
return message;
}
private List<String> getErrorList(MethodArgumentNotValidException ex) {
return ex.getBindingResult()
.getAllErrors()
.stream()
.map(ObjectError::getDefaultMessage)
.sorted()
.collect(Collectors.toCollection(ArrayList::new));
}
}
Since you're only testing your handler and not the invocation (that's Sping's job) why not just using a mock-Exception.
This way you could mock whatever properties your Exception needs to cover all the paths without having to com up with ways to create the Exception in first place...
In my main controller, when the exception is thrown, I want it to be catched by the ExceptionHandler in my error handling controller, but that never happens. Instead, I am getting Error 500. I am suspecting the problem is in #ResponseBody annotation of my main controller. Any idea how to achieve wanted behavior?
Main controller
#RequestMapping(value = "/person/{person}", method = RequestMethod.GET)
public #ResponseBody Person execute(#PathVariable(value = "person") String person) {
if(person.isValid(person)) {
return person;
} else {
throw new ResourceNotFoundException("Invalid person format.");
}
}
Exception
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException() {
}
public ResourceNotFoundException(String message) {
super(message);
}
public ResourceNotFoundException(String message, Throwable throwable) {
super(message, throwable);
}
public ResourceNotFoundException(Throwable throwable) {
super(throwable);
}
}
Error controller
private static final String ERROR_PAGE = "errors/error.jsp";
#ResponseStatus(value = HttpStatus.NOT_FOUND)
#ExceptionHandler(ResourceNotFoundException.class)
public ModelAndView invalidApiCall(){
return generateView(ERROR_404);
}
private ModelAndView generateView(String errorCode) {
return new ModelAndView(ERROR_PAGE);
}
My error view never gets generated (#ExceptionHandler never catches the exception). Instead I am getting error 500. Is there a way for ExceptionHandler to catch my exception?
Try to add #ControllerAdvice annotation for the Error Controller. If it is already added, check whether the class' package is included in package scan.
I have a controller like below
#Controller("myController")
#RequestMapping("api")
public class MyController {
#RequestMapping(method = RequestMethod.GET, value = "/get/info/{id}", headers = "Accept=application/json")
public #ResponseBody
Student getInfo(#PathVariable String info) {
.................
}
#ExceptionHandler(Throwable.class)
#ResponseStatus( HttpStatus.EXPECTATION_FAILED)
#ResponseBody
public String handleIOException(Throwable ex) {
ErrorResponse errorResponse = errorHandler.handelErrorResponse(ex);
return errorResponse.toString();
}
}
The controller has an error handling mechanism, in the error handling option it always return expectation fail status code 417. But I need to set a dynamic error Http status code like 500, 403 etc depending on type of error. How do I do this?
You need to change the type of the output value ResponseEntity. Answer here:
How to respond with HTTP 400 error in a Spring MVC #ResponseBody method returning String?
I get a solution and going to share this and also like to know any good suggestions.
#Controller("myController")
#RequestMapping("api")
public class MyController {
#RequestMapping(method = RequestMethod.GET, value = "/get/info/{id}", headers = "Accept=application/json")
public #ResponseBody
Student getInfo(#PathVariable String info) {
// ...
}
}
// ...
#ExceptionHandler(Throwable.class)
//#ResponseStatus( HttpStatus.EXPECTATION_FAILED)<<remove this line
#ResponseBody
public String handleIOException(HttpServletResponse httpRes,Throwable ex){ // <<Change this
if (some condition) {
httpRes.setStatus(HttpStatus.BAD_GATEWAY.value());
} else {
httpRes.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
}
ErrorResponse errorResponse = errorHandler.handleErrorResponse(ex);
return errorResponse.toString();
}
Expected out in rest client :
502 Bad Gateway
{
"status":"BAD_GATEWAY",
"error":"java.lang.UnsupportedOperationException",
"message":"Some error message"
}
Thanks for your replies. I still need pointers for good practices.
Going by the code above, you need to be more careful about which exceptions you are throwing and handling. Setting up an exception handler for Throwable seems overly broad.
The way I do this is to create an ErrorMessage class with my XML/JSON marshalling annotations.
#XmlRootElement(name = "error")
public class ErrorMessage {
private Throwable exception;
private String message;
public ErrorMessage() {
this.message = "";
}
public ErrorMessage(String message) {
this.message = message;
}
public ErrorMessage(Throwable exception) {
this.exception = exception;
this.message = exception.getLocalizedMessage();
}
#XmlTransient
#JsonIgnore
public Throwable getException() {
return exception;
}
public void setException(Throwable exception) {
this.exception = exception;
}
#XmlElement(name = "message")
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
With that in place, I tend to create my own application exceptions and then create my exception handler methods such as:
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseBody
#ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorMessage handleResourceNotFoundException(ResourceNotFoundException e, HttpServletRequest req) {
return new ErrorMessage(e);
}
#ExceptionHandler(InternalServerErrorException.class)
#ResponseBody
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorMessage handleInternalServerErrorException(InternalServerErrorException e, HttpServletRequest req) {
return new ErrorMessage(e);
}
With those in place, I just need to throw appropriate exceptions from my controller methods. For instance, if I throw a ResourceNotFoundException, then Spring will redirect that to my handleResourceNotFoundException method, which returns a 404, and that will also return JSON or XML representing the error.
You can use an Aspect for your API. If you define an #Around interceptor for your service, you can change the response content.
I am new to this, trying to achieve reading some docs but its not working, please bear with me.
I have created a UserNotFoundMapper using ExceptionMappers like this:
public class UserNotFoundMapper implements ExceptionMapper<UserNotFoundException> {
#Override
public Response toResponse(UserNotFoundException ex) {
return Response.status(404).entity(ex.getMessage()).type("text/plain").build();
}
}
This in my service:
#GET
#Path("/user")
public Response getUser(#QueryParam("id") String id) throws UserNotFoundException{
//Some user validation code with DB hit, if not found then
throw new UserNotFoundException();
}
The UserNotFoundException is an User-Defined Exception.
I tried this:
public class UserNotFoundException extends Exception {
//SOME block of code
}
But when I invoke the service, the UserDefinedExceptionMapper is not getting invoked. It seems I might be missing something in the UserDefinedException. How to define this exception then?
Please let me know how to define the UserNotFoundException.
You need to annotate your exception mapper with #Provider, otherwise it will never get registered with the JAX-RS runtime.
#Provider
public class UserNotFoundMapper implements
ExceptionMapper<UserNotFoundException> {
#Override
public Response toResponse(UserNotFoundException ex) {
return Response.status(404).entity(ex.getMessage()).type("text/plain")
.build();
}
}
What I usually do when creating APIs is create my own exception that extends from RuntimeException so I don't necessarily have to catch my exception.
Here's an example:
NOTE: I'm using JAX-RS with Jersey
First: create my own Exception that extends from RuntimeException.
public class ExceptionName extends RuntimeException {
private int code;
private String message;
public int getCode(){
return code;
}
public String getMessage(){
return message;
}
public ExceptionName(int code, String message) {
this.code = code;
this.message = message;
}
}
Also implement a ExceptionMapper
#Provider
public class ExceptionName implements ExceptionMapper<ExceptionName>{
#Override
public Response toResponse(ExceptionName exception) {
return Response.status(exception.getCode()).entity(exception.getMessage()).build();
}
}
And every time that I want to throw an exception I just do it like this anywhere, the exception mapper will take care of returning a response to the client consuming the API
throw new ExceptionName(500,"there was an error with something here");
One small remark , try to Use Response.Status.NOT_FOUND rather than using 404 etc. Code will be more readable and less prone to typos , the same goes for "text/plain". Below is the code that will handle exception as you mentioned.
Oh and one more thing remember to annotate your method #Produces(MediaType.TEXT_PLAIN) in your interface
public class UserNotFoundException extends Exception {
//...
}
public class UserServiceImpl implements UserService {
#Override
public Response getUser(#QueryParam("id") String id) {
final Response response;
try{
// call user method
//if everything is ok
response = Response.status(Response.Status.OK).entity(whateverYouWant).type(MediaType.TEXT_PLAIN).build();
} catch(UserNotFoundException ex) {
response = new UserNotFoundMapper().toResponse(ex);
}
return response;
}
}
In client slide you can check
public static boolean isUserExists(final Response serverResp) {
return serverResp != null && serverResp.getStatus() == Response.Status.NOT_FOUND.getStatusCode();
}