Generic Rest Service Jersey Error Handling

This tutorial explains the best practices how to handle jersey error handling. Basically we create a generic exception handler that will handle all exceptions thrown by any methods inside a rest service.

Dependencies

Add the dependencies to your project.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.memorynotfound.ws.rs.jersey</groupId>
    <artifactId>error-handling</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>JERSEY - ${project.artifactId}</name>
    <url>http://memorynotfound.com</url>
    <packaging>war</packaging>

    <properties>
        <jersey.version>2.18</jersey.version>
    </properties>

    <dependencies>
        <!-- jersey implementation library -->
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-jackson</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <!-- servlet provided by tomcat -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

Jersey Rest Service

For testing purposes we created a rest service that will trigger an RuntimeException.

package com.memorynotfound.rs;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/monitor")
@Produces(MediaType.APPLICATION_JSON)
public class MonitorRestService {

    @GET
    @Path("/check")
    public String checkMonitor() {
        throw new RuntimeException("Monitor not available");
    }

}

Custom Error Output

This object represents the error output that’ll be send to the client.

package com.memorynotfound.rs.exception;

import com.fasterxml.jackson.annotation.JsonRootName;

@JsonRootName(value = "error")
public class Error {

    private int statusCode;
    private String statusDescription;
    private String errorMessage;

    public Error(int statusCode, String statusDescription, String errorMessage) {
        this.statusCode = statusCode;
        this.statusDescription = statusDescription;
        this.errorMessage = errorMessage;
    }

    public int getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(int statusCode) {
        this.statusCode = statusCode;
    }

    public String getStatusDescription() {
        return statusDescription;
    }

    public void setStatusDescription(String statusDescription) {
        this.statusDescription = statusDescription;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }
}

Generic Jersey Error Handling

We create a GenericExceptionMapper that implements the ExceptionMapper interface. This implies us to override the toResponse() method. In this method we can create a response object that’ll be triggered when an exception occurs.

package com.memorynotfound.rs.exception.mapper;

import com.memorynotfound.rs.exception.Error;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
public class GenericExceptionMapper implements ExceptionMapper {

    @Override
    public Response toResponse(Throwable ex) {
        Response.StatusType type = getStatusType(ex);

        Error error = new Error(
                type.getStatusCode(),
                type.getReasonPhrase(),
                ex.getLocalizedMessage());

        return Response.status(error.getStatusCode())
                .entity(error)
                .type(MediaType.APPLICATION_JSON)
                .build();
    }

    private Response.StatusType getStatusType(Throwable ex) {
        if (ex instanceof WebApplicationException) {
            return((WebApplicationException)ex).getResponse().getStatusInfo();
        } else {
            return Response.Status.INTERNAL_SERVER_ERROR;
        }
    }
}

Servlet Descriptor

  1. Register the ServletContainer in your web.xml.
  2. Add a init-param for which packages jersey must scan to look for rest services to manage.
  3. Create a servlet-mapping on which the ServletContainer must listen.
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">

    <servlet>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.memorynotfound.rs</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>

</web-app>

Demo

URL: http://localhost:8080/jersey-error-handling/api/monitor/check

generic jersey error handling check

URL: http://localhost:8080/jersey-error-handling/api/monitor/not-available

generic jersey error handling not available

References

Download

You may also like...