Customize / Handling Server Side Exceptions with Error Codes using ExceptionMapper with Jersey / JAX-RS in Java
Table of Content
- Introduction
- System Requirements
- Article Prerequisites
- Introduction to WebApplicationException
- Implementing WebApplicationException and Defining Exception Mappers for Resource Exceptions and Errors
- Summary
- Downloads
- Resources
- 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
- Java SE Development Kit (JDK) 6
- Jersey implements JAX-RS 1.1
- Eclipse IDE for Java EE Developers or later
Article Prerequisites
- You must have setup all required tools and application as mentioned in article “Introduction, developing, implementing RESTful Web Services in Java”.
- Solid experience in Java Programming, including object-oriented Java and the Java streams model, is essential.
- Some experience with Java EE development, will be very helpful, but is not strictly required.
- Some experience with Eclipse.
- Experience or knowledge on RESTful Web Services. You can learn RESTful Web Service using article “Introduction, developing, implementing RESTful Web Services in Java”.
- I’ll assume that you’re comfortable installing Eclipse and Eclipse plugins [if required].
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
JerseyExceptionHandlingDemo.zip contains the JerseyExceptionHandlingDemo project we created in this tutorial.
Resources
- JerseyExceptionHandlingDemo.zip contains the JerseyExceptionHandlingDemo project we created in this tutorial.
- Read Presentation: RESTful Java Web Services With JAX-RS
- Read Article: Introduction, developing, implementing RESTful Web Services in Java
- Read Article: Sending / Transfering / Pushing Files in MultiParts / Chunks from Server to Client with RESTful Web Services in Java
- Read Article: Calling/Invoking Secure RESTful Web Service over HTTPS with JAX-RS in Java without Keystore & Truststore Information
- JSR 311- JAX-RS: Java API for RESTful Web Services.
- Project Jersey is the JAX-RS reference implementation.
- RESTful Web Services Developer’s Guide
- Architectural Styles and the Design of Network-based Software Architectures, by Roy Thomas Fielding
- Read chapter 5 of Roy Fielding’s dissertation, “Architectural Styles and the Design of Network-based Software Architectures.”
- Java API for XML Web Services (JAX-WS)
- JSR 224: Java API for XML-Based Web Services (JAX-WS) 2.0
- Web Application Description Language (WADL)
- Get more information about RFC 2616: Hypertext Transfer Protocol — HTTP/1.1
- Read the book RESTful Web Services.
- Download Restlet, the REST framework for Java.
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.
Good read. Thank you
very good