Exception handling in JAX-RS

 

Exception handling in JAX-RS

Exceptions in Java are a way to tell the client that an abnormal condition has occurred while executing specific statements in the code. In this section, you will see how a REST resource method handles exceptions.

Reporting errors using ResponseBuilder


The Response instance can hold metadata, such as the HTTP status code, along with the entity body. The REST resource method can return the Response object to report back on the status of the REST API call to the caller.

For example, the following resource method returns HTTP 404 Not Found (represented by the following Enum constant: Response.Status.NOT_FOUND) if the department entity object is not found in the data store:

@DELETE 
@Path("departments/{id}") 
public Response remove(@PathParam("id") Short id) { 
    Department department = entityManager.find(Department.class, 
        id); 
    if(department == null){ 
        //Department to be removed is not found in data store 
        return Response.status(Response.Status.NOT_FOUND).entity 
            ("Entity not found for : " + id).build();  
    } 
    entityManager.remove(entityManager.merge(department)); 
    return  Response.status(Response.Status.OK).build(); 
} 

Although the approach of using the Response object for reporting errors back to the caller works for basic use cases, it may not really scale up in real life. With this approach, every resource method in a class should have the Response object as the return type. Furthermore, developers may need to catch all exceptions in the resource method and convert them to the Response object programmatically. This may eventually result in repetitive coding, silly coding errors, and maintenance issues. JAX-RS addresses this issue by allowing you to directly throw exceptions from the REST resource methods to report errors back to the caller.


Reporting errors using WebApplicationException

The JAX-RS framework provides javax.ws.rs.WebApplicationException which you can throw from the JAX-RS resource methods or provider implementations to report exceptions back to the caller. Later, in the request processing cycle, the default exception mapper class deployed by the JAX-RS runtime will intercept the exception thrown by the method and will generate an appropriate HTTP response object. WebApplicationException can be created by wrapping the response content, error text, or HTTP status code. As WebApplicationException is extended from RuntimeException, the methods that throw this exception do not need to have the throws clause for WebApplicationException in the method signature. When a method throws WebApplicationException, the server stops the execution of the request and sends the response created from WebApplicationException back to the client. Here is an example:

@DELETE 
@Path("departments/{id}") 
public void removeDepartment(@PathParam("id") Short id) { 
    //Read department from data store for id 
    Department department = findDepartmentEntity(id); 
    //throw exception if department to be deleted is not found 
    if(department == null){ 
        throw new  
            WebApplicationException(Response.Status.NOT_FOUND); 
    } 
    removeDepartmentEntity(department); 
} 

The preceding example throws WebApplicationException with the  HTTP status 404 Not Found if the department is not found in the data store. You can use different HTTP status codes, depending upon the use case. The javax.ws.rs.core.Response.Status class holds the commonly used status codes defined by HTTP. We discussed the HTTP status codes and their meaning in the HTTP status codes section in Chapter 1Introducing the REST Architectural Style.

There are many exceptions available in JAX-RS, subclassed from javax.ws.rs.WebApplicationException. You can use the most appropriate one in your use case. Some of the exceptions extended from WebApplicationException are as follows: BadRequestExceptionForbiddenExceptionNotAcceptableExceptionNotAllowedExceptionNotAuthorizedExceptionNotFoundExceptionNotSupportedExceptionRedirectionExceptionInternalServerErrorException, and ServiceUnavailableException.

Refer to the API documentation to learn more about the child exceptions derived from WebApplicationException available at http://docs.oracle.com/javaee/7/api/javax/ws/rs/WebApplicationException.html.

You can also extend WebApplicationException to hold more meaningful error messages for your application, as shown in the following code:

import javax.ws.rs.WebApplicationException; 
import javax.ws.rs.core.MediaType; 
import javax.ws.rs.core.Response; 
 
public class DepartmentNotFoundWebAppException extends  
    WebApplicationException { 
 
    /** 
     * Generates a HTTP 404 (Not Found) exception. 
     */ 
    public DepartmentNotFoundWebAppException() { 
        super(Response.Status.NOT_FOUND); 
    } 
 
    /** 
     * Generates a HTTP 404 (Not Found) exception. 
     * @param message  
     */ 
    public DepartmentNotFoundWebAppException(String message) { 
        super(Response.status(Status.NOT_FOUND). 
        entity(message).type(MediaType.TEXT_PLAIN).build()); 
    } 
} 

WebApplicationException is subclassed from RuntimeException. This fits well for handling unexpected exceptions that may occur in the REST API implementation. However, for a typical business application, there may be many modules and certain modules may be reused in a non-REST context as well. So, you cannot live with WebApplicationException (which is automatically handled by the JAX-RS runtime) for all scenarios. The next section discusses the usage of the checked business exception in the JAX-RS application.

Reporting errors using application exceptions


It is recommended to use a checked application exception for recoverable error scenarios. In this section, we will see how a checked exception can be used in a RESTful web API implementation.

Here is a checked business exception definition for use in the JAX-RS resource method:

//Business exception class 
public class DeprtmentNotFoundBusinessException extends Exception{ 
 
    public DeprtmentNotFoundBusinessException(String message) { 
        super(message); 
    } 
 
    public DeprtmentNotFoundBusinessException(String message,  
       Throwable cause) { 
        super(message, cause); 
    } 
 
    //Rest of the implementation code goes here 
} 

The following code snippet uses DeprtmentNotFoundBusinessException for reporting the DepartmentNotFound error to the caller:

@DELETE 
@Path("departments/{id}") 
public void remove(@PathParam("id") Short id) throws  
    DeprtmentNotFoundBusinessException { 
    //Read department from data store for id 
    Department department = findDepartmentEntity(id); 
    // throw exception if department to be deleted is not found 
    if(department == null){ 
        throw new DeprtmentNotFoundBusinessException 
            ("Department is missing in store"); 
 
    } 
    removeDepartmentEntity(department); 
} 

The preceding implementation is simple and easy to follow. However, you may want to perform an additional step to map exceptions to the HTTP response body content. Note that the JAX-RS runtime, by default, does not know how to generate the right response content for the custom application exception thrown by a method. All the unhandled exceptions are handled by the underlying servlet container, which may wrap or swallow your application exception. The solution is to use the exception mapper feature offered by JAX-RS. JAX-RS allows you to deploy a custom exception mapper implementation, which will map the business exception to the appropriate response message. The next section discusses this topic in detail.

Mapping exceptions to a response message using ExceptionMapper


You can use the javax.ws.rs.ext.ExceptionMapper class to map the checked or unchecked exceptions to the appropriate HTTP response content. At runtime, when an exception is thrown by a method, the JAX-RS runtime will scan through all registered exception mappers to find the best match for handling the exception. If there is no exact match found, runtime considers a mapper class that matches with the parent class of the checked exception class. After identifying the exception mapper class for handling an exception, the framework invokes the toResponse() method on the exception mapper instance to generate the appropriate HTTP response content for the exception.

Here is an example for the ExceptionMapper class, which creates the HTTP response for DepartmentNotFoundBusinessException:

import javax.ws.rs.core.Response; 
import javax.ws.rs.ext.ExceptionMapper; 
import javax.ws.rs.ext.Provider;  
 
@Provider  
public class DepartmentNotFoundExceptionMapper implements  
    ExceptionMapper<DeprtmentNotFoundBusinessException> { 
 
    // Map an exception to a Response 
    @Override 
    public Response toResponse(DeprtmentNotFoundBusinessException 
        exception) { 
        return Response.status( 
            Response.Status.NOT_FOUND). 
        entity(exception.getMessage()).build();  
    } 
 
} 

When the runtime fails to find an exception mapper for the custom exceptions, the exception will be propagated to the underlying container, which will eventually result in javax.servlet.ServletException, sending the HTTP 500 status back to the client.



Comments

Popular posts from this blog

Understanding the JAX-RS resource life cycle

Generating a chunked output using Jersey APIs