Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

This is an example of how to make a global exception handler. Our company had a need to work with an external vendor. They needed to get error states (person not found, account disabled, insufficient access, etc). These were mapped to various HTTP status codes and they wanted a JSON body with specific information in it. Springframework provides the ability to specify an exception handler that can process exceptions for multiple controllers.

The business logic was handled by creating Exception objects for each type of exception that was needed. Each custom exception had an empty constructor and a constructor that accepts the message. This is an example:

package com.javaninja.exception;

/**
 * Missing, invalid or expired token.
 */
public class AuthorizationException extends Exception {

    /**
     * Constructs a new exception with {@code null} as its detail message. The cause is not initialized, and may
     * subsequently be initialized by a call to {@link #initCause}.
     */
    public AuthorizationException() {
    }

    public AuthorizationException(String message) {
        super(message);
    }
}

Once you have the exceptions, you are ready to create something that can handle them. Springframework used to only support exception handlers that were within the individual controllers.

As of Spring MVC 3.2, they now support an exception handler that can handle exceptions for multiple controllers via @ControllerAdvice. This takes an optional property basePackages. The basepackages property allows you to set which packages this exception handler handles exceptions for. You can not specify it for the exception handler to apply to all controllers or you can list a package or multiple packages that are comma-separated.

An example of an exception handler that handles all packages is:

@ControllerAdvice
public class MyExceptionHandler {
    //...
}

An example of an exception handler that handles a single package is:

@ControllerAdvice(basePackages = "com.javaninja.services")
public class MyExceptionHandler {
    //...
}

An example of an exception handler that handles multiple packages is:

@ControllerAdvice(basePackages = "com.javaninja.services,com.javaninja.other")
public class MyExceptionHandler {
    //...
}

Now that you have a class that can handle exceptions, it’s time to get to work. To handle an exception, you annotate a method with @ExceptionHandler. There is an optional value property. You can specify one or more exceptions to handle. If you don’t list any exceptions, the exception handler will handle all exceptions.

An example from the Springframework documentation of an exception handler method that handles a single exception is:

@ExceptionHandler(IOException.class)
public ResponseEntity<String> handleIOException(IOException ex) {
// prepare responseEntity
return responseEntity;
}

An example that handles several exceptions is:

@ExceptionHandler(value = {IOException.class, SqlException.class})
public ResponseEntity<String> handleIOException(IOException ex) {
// prepare responseEntity
return responseEntity;
}

An example that handles all exceptions is:

@ExceptionHandler
public ResponseEntity<String> handleIOException(IOException ex) {
// prepare responseEntity
return responseEntity;
}

These methods returned a ResponseEntity. This offers you a tremendous amount of flexibility. Much more than I needed in my case.

If you are working on JSON services, you can use normal things like @ResponseBody to translate an object into it’s JSON representation.

    @ExceptionHandler(IOException.class)
    @ResponseBody
    public MyExceptionResponse handleThisException(Exception e) {
        return createResponse(e);
    }

Returning a certain HTTP status code after encountering an exception isn’t an uncommon thing. To do this, you use @ResponseStatus. There is a required value of HttpStatus type. An example is:

    @ExceptionHandler(IOException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public MyExceptionResponse handleThisException(Exception e) {
        return createResponse(e);
    }

Now, to bring this all together. This is an full example:

package com.javaninja.exception;

import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
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 javax.validation.ConstraintViolationException;

/**
 * Handles exceptions for the Java Ninja.
 * @author norris.shelton
 */
@ControllerAdvice
public class ThisExceptionHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * Handle invalid request exceptions.
     * @param e exception object to be handled
     * @return exception response as a JSON body
     */
    @ExceptionHandler(ClassNotFoundException.class)
    @ResponseStatus(value = HttpStatus.NOT_FOUND)
    @ResponseBody
    public MyExceptionResponse handleBadRequestException(Exception e) {
        return createResponse(e);
    }

    /**
     * Handle authorization exceptions.
     * @param e exception object to be handled
     * @return exception response as a JSON body
     */
    @ExceptionHandler(value = {CGIServerException.class, AbandonedObjectException.class})
    @ResponseStatus(value = HttpStatus.UNAUTHORIZED)
    @ResponseBody
    public MyExceptionResponse handleAuthorizationException(Exception e) {
        return createResponse(e);
    }


    /*
     * Create the response body
     */

    /**
     * Create the response object.
     * @param e exception object to be handled
     * @return exception response object
     */
    private MyExceptionResponse createResponse(Exception e) {
        logger.info("{}  message: {}", e.getClass().getSimpleName(), e.getMessage());
        MyExceptionResponse exceptionResponse = new MyExceptionResponse(e.getClass().getSimpleName(),
                                                                        e.getMessage(),
                                                                        DateTime.now());
        logger.debug("exception response: {}", exceptionResponse);
        return exceptionResponse;
    }
}

When this exception handler encounters an exception, it returns a specific error code and the JSON payload appears similar to:

{
    "code": "TxnNotFoundException",
    "msg": "Unable to find the requested transaction",
    "timestamp": "2015-07-30 12:51:54"
}

July 30th, 2015

Posted In: Java, java ninja, Javaninja, Spring

Tags: , , ,

Leave a Comment

WP to LinkedIn Auto Publish Powered By : XYZScripts.com