How to implement a RuntimeExceptionMapper for Dropwizard

October 23, 2012 | Comments
Tags: HowTo Design Dropwizard


One common problem that web developers face is the elegant handling of exceptions in their applications. You want to give the browser the correct response code (e.g. 404 or 500), but you also want to ensure that the user experience of that failure maintains the style of the overall site. The default Dropwizard behaviour is rather minimal in this area (by design) and so I thought I’d post up an implementation to act as a starting point for others to use:

import com.yammer.dropwizard.logging.Log;
import org.multibit.store.views.PublicFreemarkerView;

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

/**
 * <p>Provider to provide the following to Jersey framework:</p>
 * <ul>
 * <li>Provision of general runtime exception to response mapping</li>
 * </ul>
 */
@Provider
public class RuntimeExceptionMapper implements ExceptionMapper<RuntimeException> {

  private static final Log LOG = Log.forClass(RuntimeExceptionMapper.class);

  @Override
  public Response toResponse(RuntimeException runtime) {

    // Build default response
    Response defaultResponse = Response
      .serverError()
      .entity(new PublicFreemarkerView("error/500.ftl"))
      .build();

    // Check for any specific handling
    if (runtime instanceof WebApplicationException) {

      return handleWebApplicationException(runtime, defaultResponse);
    }

    // Use the default
    LOG.error(runtime, runtime.getMessage());
    return defaultResponse;

  }

  private Response handleWebApplicationException(RuntimeException exception, Response defaultResponse) {
    WebApplicationException webAppException = (WebApplicationException) exception;

    // No logging
    if (webAppException.getResponse().getStatus() == 401) {
      return Response
        .status(Response.Status.UNAUTHORIZED)
        .entity(new PublicFreemarkerView("error/401.ftl"))
        .build();
    }
    if (webAppException.getResponse().getStatus() == 404) {
      return Response
        .status(Response.Status.NOT_FOUND)
        .entity(new PublicFreemarkerView("error/404.ftl"))
        .build();
    }

    // Debug logging

    // Warn logging

    // Error logging
    LOG.error(exception, exception.getMessage());

    return defaultResponse;
  }

}

The response contains an entity (an instance of PublicFreemarkerView) which is simply a View that references a Freemarker template stored under src/main/resources/assets/views/ftl. The code is trivial, but I’ll include it here for completeness:

public class PublicFreemarkerView extends View {

  public PublicFreemarkerView(String templateName) {
    super("/views/ftl/"+templateName);
  }

}

By adding the above (or including it in the scan) as a provider during startup you can rely on all your accidental RuntimeExceptions getting a decent presentation.

Tweet

Related Posts

Useful? Consider a small donation: 1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH

Got an opinion? Comments are welcome!

Want more? Take a look at the following:

Dropwizard with Ember Data demo
Dropwizard with OpenID
How to include Markdown with Freemarker in Dropwizard
MultiBit Merchant: Implementing HMAC authentication in Dropwizard
How to deploy a Dropwizard project to Heroku
How to build a Builder
MultiBit Merchant: Deployment Driven Design?
Do you need a Code Librarian?
7 refactorings I couldn't live without
Stored procedures vs Hibernate
Creating agile functional tests with Selenium
How to be agile when all about you are not - part 2
How to be agile when all about you are not - part 1
How to create a deterministic JAR
Preventing Dependency Chain Attacks in Maven
How to deploy dynamic sites with git
How to deploy static sites with git
How to recover your bitcoins from a failed hard drive
How to accept bitcoins on your blog with no code

comments powered by Disqus