Wednesday, August 29, 2018

Consistent Error Handling in a Spring Boot REST Service

Setting up a REST service using Spring Boot is simple. However, setting up consistent error handling for that service can be a bit more daunting. You'll typically want your service to use one particular error structure (JSON) for every error generated by the application. The question is: how do you do that in the simplest possible way with all the options you have available?
  • Option 1 - Servlet API Error Handling - The Servlet API itself contains an error handling system configured via web.xml. You can use the <error-page> element to specify the response handler (a JSP, Servlet, ...) for certain exceptions and HTTP status codes. In practice this means that if a Servlet or JSP generates an exception or calls HttpServletResponse.sendError(), the corresponding <error-page> page will be sent back to the client.
  • Option 2 - Spring MVC Error Handling - Spring MVC's DispatcherServlet comes with it's own error handling system, implemented using a HandlerExceptionResolver. In modern Spring applications this is typically setup using @ExceptionHandler @ControllerAdvice. When a request handler throws and exception, an appropriate exception handler is selected and tasked with resolving the error by generating an error response. Note that if an exception handler resolves the error, the exception never propagates up to the Servlet engine, meaning the defined Servlet error pages are not considered.
  • Option 3 - Spring Boot Error Handling - Spring Boot comes with a default error handling setup built on top of the Servlet API and Spring MVC error handling systems. The default setup (see ErrorMvcAutoConfiguration) does three things:
    1. It configures a Servlet <error-page> directed at /error for all exceptions and response codes.
    2. It defines an ErrorController handling /error requests, forwarded to it because of the configured Servlet error page. The default ErrorController implementation is the BasicErrorController, which will try to resolve an "error" view used to render a model prepared by an ErrorAttributes implementation. If the client is requesting text/html, the default whitelabel error page (see below) will be used, otherwise the model will be rendered directly as response body (typically JSON).
    3. The whitelabel error page serves as a simplistic HTML "error" view detailing the error that occurred by rendering the model prepared by the ErrorAttributes implementation.

That's a lot to take in! Customizing the default error structure (see DefaultErrorAttributes) involves several steps:

  1. Define a class for your own error structure, say ErrorInfo.
  2. Implement approprate @ExceptionHandlers returning ResponseEntity<ErrorInfo> objects, typically in a ResponseEntityExceptionHandler subclass.
  3. Implement an ErrorAttributes bean to return a Map (ugh!) corresponding with your ErrorInfo structure.
  4. Replace the whitelabel error page with one that can handle your ErrorInfo structure.
Although steps 1 and 2 make sense, steps 3 and 4 feel clumsy and unelegant.

Luckily there is a simpler way to do this if you're implementing a pure REST service that's only concerned with returning JSON responses, while still leveraging part of the default setup done by Spring Boot (ErrorMvcAutoConfiguration, specifically the /error Servlet error page).

  1. Of course you still need to define your own ErrorInfo structure.
  2. And you'll also still need to implement appropriate @ExceptionHandlers returning ResponseEntity<ErrorInfo> objects.
  3. Now implement an ErrorController that handles /error requests, returning an appropriate ResponseEntity<ErrorInfo>.
  4. Finally, disable the whitelabel error page by setting the server.error.whitelabel.enabled property to false.
That feels quite a bit better: all code uses your own ErrorInfo class to render an error and you don't need to spend time implementing an HTML error view for an application that should never output HTML anyway.

Friday, August 17, 2018

Git and Command Line Productivity for Windows

If you're stuck on Windows (which I have to admit is not that bad nowadays), and want an easy and productive way to work with Git on the command line, I highly recommend Cmder!

Cmder doesn't need administrative privileges to be installed: you can simply unzip it and of you go! This makes it ideal for use on highly locked down machines you might find at some clients, typically large companies with strict computer policies. If you download the "full" version, it even comes with Git for Windows built in, along with a host of Unix utilities (grep, find, ...) giving you a very powerful and configurable tabbed command line experience.

Really an excellent piece of software that I highly recommend!

Friday, March 2, 2018

Nested Enums

Little known fact that I also only just found out about: nested enum types are implicitly static in Java (see the JLS). However, it is permitted for the declaration of a nested enum type to redundantly specify the static modifier. In other words, the following two enum declarations are effectively equivalent:
public class Foo {

   public static enum Bar {
   }
}


public class Foo {

   public enum Bar {
   }
}

Thursday, May 18, 2017

File.listFiles Gotcha

I had another WTF!? moment trying to get to the bottom of a NullPointerException we've been seeing. It turns out File.listFiles returns null when you're trying to list something that's not actually a directory, or when an I/O error occurs!
/**
 * Returns an array of abstract pathnames denoting the files in the
 * directory denoted by this abstract pathname.

...

 * @return  An array of abstract pathnames denoting the files and
 *          directories in the directory denoted by this abstract pathname.
 *          The array will be empty if the directory is empty.  Returns
 *          {@code null} if this abstract pathname does not denote a
 *          directory, or if an I/O error occurs.
 *
 * @throws  SecurityException
 *          If a security manager exists and its {@link
 *          SecurityManager#checkRead(String)} method denies read access to
 *          the directory
 *
 * @since  1.2
 */
public File[] listFiles() {
Good luck trying to find out which I/O error occurred! I'm guessing just throwing an IOException would have been too easy. Luckily Java 8 provides Files.list as an alternative which does throw an IOException if needed.

Friday, November 25, 2016

Java 8 Update of Bitemporal Framework

Just to push this out to the world a bit more: I've updated the "com.ervacon.bitemporal" framework to bring it up-to-date with current-day Java:
  • Use Java 8's new time classes instead of JodaTime
  • Leverage Java 8's goodies where relevant: streams, diamond operator, ...
  • Switched to Hibernate 5
  • Use JUnit 4
Just as before, "com.ervacon.bitemporal" provides a good starting point for those that need to tackle temporal issues in their applications. For more information, check the GitHub page: https://github.com/klr8/bitemporal.

Monday, May 23, 2016

Java Bean Validation Gotcha

A well known trick for doing cross field validation using Java Bean Validation (JSR-303) is to simply put an @AssertTrue annotation on a method that actually does the validation, like so:
public class Document {

   @NotNull
   private Status status;   
   private Date signingDate;

   @AssertTrue(message = "A signed document should have a signing date")
   public boolean isASignedDocumentHasASigningDate() {
      return status != SIGNED || signingDate != null;
   }
}
The fact that the assertion method actually returns a boolean also makes unit testing easy. So far so good!

However, there is a little known gotcha related to this: the annotated validation method actually has to follow the naming conventions of a boolean bean property getter, i.e. getBla() or isBla(). In other words the following would not work:

   @AssertTrue(message = "A signed document should have a signing date")
   public boolean aSignedDocumentHasASigningDate() {
      return status != SIGNED || signingDate != null;
   }
That's quite unfortunate, and certainly doesn't follow the principle of least astonishment: we're explicitly annotating the method so there is no reason why the bean validator should not pick it up! Even worse, unless you're thoroughly testing all you bean validation annotations, it's easy to miss the fact that the validation is not actually happening. Something to keep in mind!

Friday, December 11, 2015

The Benefits of Building on Multiple Platforms

Organizational policy at a client of mine recently saw me switching my development setup from an Ubuntu Linux based machine to Windows 7 Enterprise. Before this switch everything was Linux based: development was done in Linux, continuous integration ran on Linux, and we were ultimately also deploying to Linux, albeit another distribution.

Making the switch to Windows unearthed a few subtle coding errors, mainly related to resource management. It's no secret Windows is a lot stricter when it comes to file manipulation. For instance, consider the following:

File f = new File("test.tmp");
try (FileOutputStream fout = new FileOutputStream(f)) {
 fout.write("foo".getBytes());
 fout.flush();
 Files.move(f.toPath(), new File("test.txt").toPath());
}
Using Java 8, this runs fine on Linux. However, on Windows you get an error:
java.nio.file.FileSystemException: test.tmp -> test.txt: The process cannot access the file because it is being used by another process.
Looking at the code again you can see that the file is being moved inside the try-with-resources block. In other words, we're attempting to move the file before we've closed the output stream!

In general I would advise different developers in your development team to use different platforms. That will highlight these kind of subtle errors early and will generally improve the quality of your system. It of course comes as little surprise that Juergen Hoeller (of Spring fame) told me on several occasions that he's still developing on Windows for exactly this reason! :-)