Customize / Handling Server Side Exceptions with Error Codes using ExceptionMapper with Jersey / JAX-RS in Java

The article is written for/using J2SE 6, Jersey 1.1.4.1 and Eclipse IDE for Java EE Developers [Ganymede].

Table of Content

  1. Introduction
  2. System Requirements
  3. Article Prerequisites
  4. Introduction to WebApplicationException
  5. Implementing WebApplicationException and Defining Exception Mappers for Resource Exceptions and Errors
  6. Summary
  7. Downloads
  8. Resources
  9. Feedback

Introduction

JAX-RS is a collection of interfaces and Java annotations that simplifies development of server-side REST applications. Representational State Transfer (REST) applications are easier to develop and easier to consume when compared to other types of distributed systems.

JAX-RS applications can produce exceptions and errors. This post covers how to customize or handle server side checked and unchecked exceptions using Jersey ExceptionMapper.


System Requirements


Article Prerequisites


Introduction to WebApplicationException

JAX-RS introduced the exception, javax.ws.rs.WebApplicationException. You can specify a specific error class name or javax.ws.rs.core.Response object when creating javax.ws.rs.WebApplicationException. When the javax.ws.rs.WebApplicationException is thrown, the information included in the exception by way of a status class name or Response object is used to serialize a response.

This exception may be thrown by a resource method, provider or javax.ws.rs.core.StreamingOutput implementation if a specific HTTP error response needs to be produced.


Implementing WebApplicationException and Defining Exception Mappers for Resource Exceptions and Errors

To get started we will create a simple Web Service Project by selecting Dynamic Web Project from the new toolbar menu. Alternatively, invoke the wizard using File > New > Dynamic Web Project.

Name the project JerseyExceptionHandlingDemo and follow step by step instructions on setting eclipse project and adding Jersey libraries, mentioned here.

Add Jersey Libraries to project. For very detailed, step by step instructions on setting eclipse project and adding Jersey libraries, refer here.

Click and open JerseyExceptionHandlingDemo > WebContent > WEB-INF > web.xml. Edit your web.xml and add following code snippet.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="JerseyExceptionHandlingDemo" version="2.5">
	<display-name>JerseyExceptionHandlingDemo</display-name>
	<description>Demo service to show/test Jersey Exception Handling.</description>
	<servlet>
		<display-name>Bhavesh Thaker's Jersey Exception Handling Demo Servlet</display-name>
		<servlet-name>JerseyExceptionHandlingDemo Servlet</servlet-name>
		<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>JerseyExceptionHandlingDemo Servlet</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>
</web-app>

To start, create a simple Book class with id, bookName, bookAuthor and bookISBN fields; this class represents the Book entity we will be managing with our web service. Use the File > New > Class wizard, put Book in the Name field, com.bhaveshthaker.restws.exceptionhandlingdemo in the Package field and Finish the wizard. Replace the contents of the generated class with the following code:

/**
 * Copyright (c) 2008 - 2011 Bhaveshkumar Thaker [http://bhaveshthaker.com].  All rights reserved.
 */
package com.bhaveshthaker.restws.exceptionhandlingdemo;

import javax.xml.bind.annotation.XmlRootElement;

/**
 * Book class.
 * 
 * @author <a href="http://bhaveshthaker.com/">Bhaveshkumar Thaker</a>
 */
@XmlRootElement(name = "book")
public class Book {

	int bookId = 0;
	String bookAuthor = "";
	String bookName = "";
	String bookISBN = "";

	public int getBookId() {
		return bookId;
	}

	public void setBookId(int bookId) {
		this.bookId = bookId;
	}

	public String getBookAuthor() {
		return bookAuthor;
	}

	public void setBookAuthor(String bookAuthor) {
		this.bookAuthor = bookAuthor;
	}

	public String getBookName() {
		return bookName;
	}

	public void setBookName(String bookName) {
		this.bookName = bookName;
	}

	public String getBookISBN() {
		return bookISBN;
	}

	public void setBookISBN(String bookISBN) {
		this.bookISBN = bookISBN;
	}

}

JAX-RS introduced the exception, javax.ws.rs.WebApplicationException. A developer can specify a specific error class name or javax.ws.rs.core.Response object when creating a WebApplicationException. When the WebApplicationException is thrown, the information included in the exception by way of a status class name or Response object is used to serialize a response.

Now, let’s create the BookStoreThrowingWebAppException class. Use the File > New > Class wizard, put BookStoreThrowingWebAppException in the Name field, com.bhaveshthaker.restws.exceptionhandlingdemo in the Package field and Finish the wizard. Replace the contents of the generated class with the following code:

/**
 * Copyright (c) 2008 - 2011 Bhaveshkumar Thaker [http://bhaveshthaker.com].  All rights reserved.
 */
package com.bhaveshthaker.restws.exceptionhandlingdemo;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import com.sun.jersey.spi.resource.Singleton;

/**
 * @author <a href="http://bhaveshthaker.com/">Bhaveshkumar Thaker</a>
 * 
 */
@Path("bookstorethrowingwebappexception")
@Produces("application/xml")
@Singleton
public class BookStoreThrowingWebAppException {

	@GET
	@Path("{id}")
	@Produces("application/xml")
	public Book getBook(@PathParam("id") int bookId) {
		if (bookId != 1) {
			throw new WebApplicationException(Response.status(
					Response.Status.INTERNAL_SERVER_ERROR).type(
					MediaType.TEXT_PLAIN).entity(
					"Book, " + bookId + ", is not found").build());
		}

		Book myBook = new Book();
		myBook.setBookId(bookId);
		myBook.setBookAuthor("Bhavehkumar Thaker");
		myBook
				.setBookName("Introduction to exception handling in JAX-RS RESTful Web Services");
		myBook.setBookISBN("ISBN 10: 0-596-52926-0");

		return myBook;
	}

}

The following example shows the throwing of a BookNotFoundException from the above sample instead of direct javax.ws.rs.WebApplicationException. This exception is a Jersey specific exception that extends javax.ws.rs.WebApplicationException and builds a HTTP response with the 404 status code and an optional message as the body of the response.

Now, let’s create the BookStoreThrowingCustomWebAppException class. Use the File > New > Class wizard, put BookStoreThrowingCustomWebAppException in the Name field, com.bhaveshthaker.restws.exceptionhandlingdemo in the Package field and Finish the wizard. Replace the contents of the generated class with the following code:

/**
 * Copyright (c) 2008 - 2011 Bhaveshkumar Thaker [http://bhaveshthaker.com].  All rights reserved.
 */
package com.bhaveshthaker.restws.exceptionhandlingdemo;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import com.sun.jersey.spi.resource.Singleton;

/**
 * Class demonstrates throwing Jersey specific exceptions to control response.
 * 
 * @author <a href="http://bhaveshthaker.com/">Bhaveshkumar Thaker</a>
 * 
 */
@Path("bookstorethrowingcustomwebappexception")
@Produces("application/xml")
@Singleton
public class BookStoreThrowingCustomWebAppException {

	@GET
	@Path("{id}")
	@Produces("application/xml")
	public Book getBook(@PathParam("id") int bookId) {
		if (bookId != 1) {
			throw new BookNotFoundException("Book, " + bookId
					+ ", is not found");
		}

		Book myBook = new Book();
		myBook.setBookId(bookId);
		myBook.setBookAuthor("Bhavehkumar Thaker");
		myBook
				.setBookName("Introduction to exception handling in JAX-RS RESTful Web Services");
		myBook.setBookISBN("ISBN 10: 0-596-52926-0");

		return myBook;
	}

	/**
	 * Jersey specific exception implementation.
	 * 
	 * Custom <code>BookNotFoundException</code> exception class which extends
	 * <code>WebApplicationException</code>.
	 * 
	 * @author <a href="http://bhaveshthaker.com/">Bhaveshkumar Thaker</a>
	 */
	public class BookNotFoundException extends WebApplicationException {

		private static final long serialVersionUID = 1L;

		/**
		 * Create a HTTP 404 (Not Found) exception.
		 */
		public BookNotFoundException() {
			super(Response.status(Response.Status.INTERNAL_SERVER_ERROR)
					.build());
		}

		/**
		 * Create a HTTP 404 (Not Found) exception with custom message.
		 * 
		 * @param message
		 *            the String that is the entity of the 404 response.
		 */
		public BookNotFoundException(String message) {
			super(Response.status(Response.Status.NOT_FOUND).entity(message)
					.type(MediaType.TEXT_PLAIN).build());
		}

	}

}

In other cases it may not be appropriate to throw instances of javax.ws.rs.WebApplicationException, or classes that extend javax.ws.rs.WebApplicationException, and instead it may be preferable to map an existing exception to a response. For such cases it is possible to use the ExceptionMapper interface.

If you cannot throw the exception, WebApplicationException, in your code and you cannot use the error handling facilities in the web container, but you want to use a custom error response, then you can create a customized JAX-RS javax.ws.rs.ext.ExceptionMapper class to map exceptions to HTTP error responses. The following procedure illustrates how to write a custom ExceptionMapper class.

Create a class BookNotFoundCustomException that implements the javax.ws.rs.ext.ExceptionMapper class and annotate the class with the javax.ws.rs.ext.Provider annotation. This step assumes that your JAX-RS resource can throw the exception, BookNotFoundCustomException, in its methods. The following example illustrates a simple ExceptionMapper class. In the toResponse(BookNotFoundCustomException) method, return a Response object that contains the customized error response. The following example illustrates a customized ExceptionMapper.toResponse(MyCustomException) method.

Now, let’s create the BookNotFoundCustomException class. Use the File > New > Class wizard, put BookNotFoundCustomException in the Name field, com.bhaveshthaker.restws.exceptionhandlingdemo in the Package field and Finish the wizard. Replace the contents of the generated class with the following code:

/**
 * Copyright (c) 2008 - 2011 Bhaveshkumar Thaker [http://bhaveshthaker.com].  All rights reserved.
 */
package com.bhaveshthaker.restws.exceptionhandlingdemo;

/**
 * @author <a href="http://bhaveshthaker.com/">Bhaveshkumar Thaker</a>
 * 
 */
public class BookNotFoundCustomException extends Exception {

	private static final long serialVersionUID = 1L;

	/**
	 * Default constructor.
	 */
	public BookNotFoundCustomException() {
		super("Book is not found");
	}

	/**
	 * Constructor with custom message and cause.
	 * 
	 * @param customMessage
	 *            Exception message.
	 */
	public BookNotFoundCustomException(String customMessage) {
		super(customMessage);
	}

	/**
	 * Constructor with custom message and cause.
	 * 
	 * @param customMessage
	 *            Exception message.
	 * @param cause
	 *            Exception cause.
	 */
	public BookNotFoundCustomException(String customMessage, Throwable cause) {
		super(customMessage, cause);
	}

}

The class will be annotated with @Provider, this declares that the class is of interest to the JAX-RS runtime. When an application throws an BookNotFoundCustomException the toResponse method of the BookNotFoundCustomExceptionMapper instance will be invoked.

Now, let’s create the BookNotFoundCustomExceptionMapper class. Use the File > New > Class wizard, put BookNotFoundCustomExceptionMapper in the Name field, com.bhaveshthaker.restws.exceptionhandlingdemo in the Package field and Finish the wizard. Replace the contents of the generated class with the following code:

/**
 * Copyright (c) 2008 - 2011 Bhaveshkumar Thaker [http://bhaveshthaker.com].  All rights reserved.
 */
package com.bhaveshthaker.restws.exceptionhandlingdemo;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

/**
 * Mapping generic exceptions to responses.
 * 
 * @author <a href="http://bhaveshthaker.com/">Bhaveshkumar Thaker</a>
 * 
 */
@Provider
public class BookNotFoundCustomExceptionMapper implements
		ExceptionMapper<BookNotFoundCustomException> {

	/**
	 * Default constructor.
	 */
	public BookNotFoundCustomExceptionMapper() {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.ws.rs.ext.ExceptionMapper#toResponse(java.lang.Throwable)
	 */
	@Override
	public Response toResponse(
			BookNotFoundCustomException bookNotFoundCustomException) {

		return Response.status(Response.Status.NOT_FOUND).entity(
				bookNotFoundCustomException.getMessage()).type(
				MediaType.TEXT_PLAIN).build();

	}

}

Now, let’s create the BookStoreThrowingCustomMapperException class which will throw our custom exception, BookNotFoundCustomException. Use the File > New > Class wizard, put BookStoreThrowingCustomMapperException in the Name field, com.bhaveshthaker.restws.exceptionhandlingdemo in the Package field and Finish the wizard. Replace the contents of the generated class with the following code:

/**
 * Copyright (c) 2008 - 2011 Bhaveshkumar Thaker [http://bhaveshthaker.com].  All rights reserved.
 */
package com.bhaveshthaker.restws.exceptionhandlingdemo;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import com.sun.jersey.spi.resource.Singleton;

/**
 * @author <a href="http://bhaveshthaker.com/">Bhaveshkumar Thaker</a>
 * 
 */
@Path("bookstorethrowingcustommapperexception")
@Produces("application/xml")
@Singleton
public class BookStoreThrowingCustomMapperException {

	@GET
	@Path("{id}")
	@Produces("application/xml")
	public Book getBook(@PathParam("id") int bookId)
			throws BookNotFoundCustomException {
		if (bookId != 1) {
			throw new BookNotFoundCustomException("Book, " + bookId
					+ ", is not found");
		}

		Book myBook = new Book();
		myBook.setBookId(bookId);
		myBook.setBookAuthor("Bhavehkumar Thaker");
		myBook
				.setBookName("Introduction to exception handling in JAX-RS RESTful Web Services");
		myBook.setBookISBN("ISBN 10: 0-596-52926-0");

		return myBook;
	}

}

When exceptions occur in your JAX-RS resource methods, you can customize the HTTP error response so that a user cannot see a stack trace or potentially confidential data. Use an ExceptionMapper or the exception handling functionality in the web container to give more helpful responses if the application is not behaving correctly.


Summary

We have written a custom ExceptionMapper to handle exceptions in your JAX-RS web application. I recommend to be cautious about including exception stack straces in responses to external users/clients, because there could be lots of information that might be used by a potential attacker.


Downloads

Downloads

JerseyExceptionHandlingDemo.zip contains the JerseyExceptionHandlingDemo project we created in this tutorial.

Resources


Feedback

I would like to hear from you! If you liked this tutorial, have some suggestions or even some corrections for me, please let me know. I track all user feedback in comments sections.


2 Comments

  1. newbie says:

    Good read. Thank you

    • Anuj awasthi says:

      very good

Leave a Reply

Your email address will not be published. Required fields are marked *