javascriptjavaphprubydotnet

Java handling exceptions

Exception handling in Java

The classic definition of an exception is an event that occurs during the execution of a program and that disrupts the normal flow of instructions. Exceptions are specialized events that indicate something bad has happened in the application, and the application either needs to recover or exit.

Why handle exceptions?

Exception handling is important because it helps maintain the normal, desired flow of the program even when unexpected events occur. If exceptions are not handled, programs may crash or requests may fail. This can very frustrating for customers and if it happens repeatedly, you could lose those customers.

The worst situation is if your application crashes while the user is doing any important work, especially if their data is lost. To make the user interface robust, it is important to handle exceptions to prevent the application from unexpectedly crashing and losing data. There can be many causes for a sudden crash of the system, such as incorrect or unexpected data input. For example, if we try to add two users with duplicate IDs to the database, we should throw an exception since the action would affect database integrity.

Developers can predict many of the exceptions that a piece of code is capable of throwing.

The best course of action is to explicitly handle those exceptions to recover from them gracefully. As we will see ahead, programming languages provide ways to handle exceptions starting from specific ones and moving toward the more generic ones. Exceptions that you cannot easily predict ahead of time are called unhandled exceptions. It’s good to capture these too in order to gracefully recover. Tracking these exceptions centrally offers visibility to your development team on the quality of the code and what causes these errors so they can fix them faster.

Tracking

One thing that most experienced developers will agree with is that exceptions will happen in your application, no matter how well you write your program. You cannot predict the runtime environment entirely, especially that of your customer’s.

The standard practice is to record all events in the application log file. The log file acts as a time machine helping the developer (or analyst) go back in time to view all the phases the application went through and what led to the exception.

For small-scale applications, going through a log file is easy. However, enterprise applications can serve thousands or even millions of requests per second. This makes manual analysis too cumbersome because errors and their causes can be lose in the noise. This is where error monitoring software can help by grouping duplicates and providing summarized views of the top and most recent errors. They can also capture and organize contextual data in a way that’s easier and faster than looking at logs.

How to handle exceptions in Java

Let's start with the basics of exception handling before we move to more advanced topics. The try-catch is the simplest method of handling exceptions. Put the code you want to run in the try block, and any exceptions that the code throws are caught by one or more catch blocks.

This method will catch any type of exceptions that get thrown. This is the simplest mechanism for handling exceptions.

try {
  // block of code that can throw exceptions
} catch (Exception ex) {
  // Exception handler
}

Note: You can’t use a try block alone. The try block should be immediately followed either by a catch or finally block.

Catching specific exceptions

You can also specify specific exceptions you would like to catch. This allows you to have dedicated code to recover from those errors. For example, some errors returned from a REST API are recoverable and others are not. This allows you to treat those conditions separately.

A try block can be followed by one or more catch blocks, each specifying a different type. The first catch block that handles the exception class or one of its superclasses will be executed. So, make sure to catch the most specific class first.

try {
  // block of code that can throw exceptions
} catch (ExceptionType1 ex1) {
  // exception handler for ExceptionType1
} catch (ExceptionType2 ex2) {
  // Exception handler for ExceptionType2
}

If an exception occurs in the try block, the exception is thrown to the first catch block. If not, the exception passes down to the second catch statement. This continues until the exception either is caught or falls through all catches.

The finally block

Any code that must be executed irrespective of occurrence of an exception is put in a finally block. In other words, a finally block is executed in all circumstances. For example, if you have an open connection to a database or file, use the finally block to close the connection even if the try block throws an exception.

try {
  // block of code that can throw exceptions
} catch (ExceptionType1 ex1) {
  // exception handler for ExceptionType1
} catch (ExceptionType2 ex2) {
  // Exception handler for ExceptionType2
} finally {
  // finally block always executes
}

Handling uncaught or runtime exceptions

Uncaught or runtime exceptions happen unexpectedly, so you may not have a try-catch block to protect your code and the compiler cannot give you warnings either. Thankfully, Java provides a powerful mechanism for handling runtime exceptions. Every Thread includes a hook that allows you to set an UncaughtExceptionHandler. Its interface declares the method uncaughtException(Thread t1, Throwable e1). It will be invoked when a given thread t1 terminates due to the given uncaught exception e1.

Thread.setDefaultUncaughtExceptionHandler(new
Thread.UncaughtExceptionHandler() {
  public void uncaughtException(Thread t1, Throwable e1) {
  // Exception handling code  
  }
});  

Exception handling in Spring

The Spring Framework is one of the most popular Java EE frameworks using the Model-View-Controller (MVC) architecture. It is frequently used to create web applications. It is possible for the user to get an exception while using the web application. Handling those exceptions in server-side applications is important as it improves the user’s experience. The Spring MVC Framework provides the following ways to help us achieve robust exception handling.

Using ControllerAdvice

When you create a class annotated with @ControllerAdvice, it will handle exceptions created by all your controllers. Each controller advice defines a method with an @ExceptionHandler annotation which becomes the default handler. You can insert your custom code to print or track errors there.

@ControllerAdvice
class GlobalDefaultExceptionHandler {
  @ExceptionHandler(value = Exception.class)
  public ModelAndView
  defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
  // handle error here
  }
}

Using HandlerExceptionResolver

The Spring Framework also provides a HandlerExceptionResolver interface that you can implement to create a global exception handler. As it has been around since early releases, it should be compatible with both old and new code. Spring Framework makes it easy to set up by providing a SimpleMappingExceptionResolver, which maps exceptions to views. You can use this to display custom error messages to users. We’ll show you how to override it to create your own global handler which will track exceptions.

Warning: Be careful about mixing both approaches in the same application. Most applications use one approach, and using two may result in unexpected behavior.

The example below shows you how to override the SimpleMappingExceptionResolver. It allows you to create a custom method to build a log message and to return a view to the user with a more friendly error page.

public class MyMappingExceptionResolver extends SimpleMappingExceptionResolver {
  String accessToken = "8e194f5f31db4ff1b4e3e0951a40c936";
  Rollbar rollbar;

  public MyMappingExceptionResolver() {

    setWarnLogCategory(MyMappingExceptionResolver.class.getName());
  }

  @Override
  public String buildLogMessage(Exception e, HttpServletRequest req) {

    System.out.println("Exception : " + e.toString());
    RequestProvider requestProvider = new RequestProvider.Builder()
                                .userIpHeaderName(req.getRemoteAddr()).build();
    rollbar = Rollbar.init(withAccessToken(accessToken)
                                .request(requestProvider).build());
    rollbar.error(e);
  return "MVC exception: " + e.getLocalizedMessage();
  }

  @Override
  protected ModelAndView doResolveException (HttpServletRequest req,
                HttpServletResponse resp, Object handler,  Exception ex) {

    ModelAndView mav = super.doResolveException(req, resp, handler, ex);
    mav.addObject("url", req.getRequestURL());
  return mav;
  }
}

In order to make use of this class, you must configure it in your bean configuration file. Here we map in a default error page called "error." We also pass in the exception attribute, which will give our view class access to the exception object for reporting.

<bean id="simpleMappingExceptionResolver"  class="com.example.controller.MyMappingExceptionResolver">
  <property name="exceptionMappings">
    <props>
      <prop key="java.lang.Exception">error</prop>
    </props>
  </property>
  <property name="defaultErrorView" value="error" />
  <property name="exceptionAttribute" value="ex" />
</bean>