by Niranjan R. Kamath

Patch an exception-handling framework

news
Mar 21, 20059 mins

Decide at runtime how to handle exceptions

The discussions on exception handling seem endless. Many articles and white papers talk about how to handle exceptions. However, what is missing is an extensible, generic exception-handling framework that provides the user with the ability to decide at runtime how to handle exceptions.

The following problems are generally associated with exceptions and their handlers:

  • Who decides what should be done when an exception occurs in the system? If the developer decides and the client disagrees, what should be done?
  • What details should an exception convey?
  • How can we define exception handling behavior at runtime without modifying the application?

Even if we fix the way an exception should be handled, how do we ensure that the developer actually follows the rule? Being a developer myself, I know it’s difficult to follow coding rules. I decided to develop a small exception-handling framework, called Patch, that solves the above problems.

The proposed exception-handling framework provides a completely extensible solution. This framework is developed in Java and is freely available for reuse and further development.

Now let’s see how the Patch framework solves each exception problem.

Who decides what should be done when an exception occurs in the system?

This article’s exception-handling framework proposes that the client should be able to override the default action performed when an exception occurs, thereby ensuring that, when an exception does occur, the system’s behavior suits both the client and the developer. This behavior should be kept outside the main system so that, when the client changes how an exception is handled, the system’s main business logic is not affected.

The behavior expected from the exception handler shall hence be defined when the system actually deploys and not during development.

To achieve this behavior, the Patch framework provides a separate set of classes that are used to delegate an exception to the appropriate exception handler. The Patch framework assumes that the following points are always true:

  • The information an exception conveys can be finalized at design time.
  • Every exception handler is expected to handle the exception. The interface between the application and the exception handler can be finalized at design time.

The decision of which exception handler to use is made at runtime using some properties in a configuration file. Taking the above two points into consideration, the Patch framework defines two interfaces:

  • An interface that every exception should implement
  • An interface that every exception handler should implement

The PatchBaseExceptionIntf interface must be implemented by all exceptions that need to be handled by the Patch framework:

 package com.patch.framework.exceptionhandling.model;
   public interface PatchBaseExceptionIntf
   {
      public void setErrorDetails(ErrorDetails errorDetails);
      public ErrorDetails getErrorDetails();
      public String toString();
   }

Note that this interface uses an object type called ErrorDetails, which I will discuss further in the following section.

The interface that every exception handler must implement is the ExceptionHandler interface:

 package com.patch.framework.exceptionhandling;
   public interface ExceptionHandler
   {
      public void handleException(PatchBaseExceptionIntf patchBaseException);
   }

In Java, an interface just defines the expected base functionality. It does not define how the class accepting the contract should implement that functionality. By using these two interfaces, PatchBaseExceptionIntf and ExceptionHandler, the Patch framework ensures that the contract between the main application and the exception-handler classes is established.

What details should an exception convey?

Every exception always has three attributes:

  • Error code
  • Error message
  • Base exception

Error code and error message are self explanatory. A base exception comes into play when an exception wraps another exception before being thrown.

Apart from these three attributes, it is difficult to tell what a system might want to convey in its exception details. Therefore, I decided to include a fourth attribute that could accommodate any kind of information. The best suitable candidate for this requirement is the java.lang.Object.

The ErrorDetails class, shown earlier in the PatchBaseExceptionIntf interface, represents that fourth attribute. Here’s what it looks like:

 package com.patch.framework.exceptionhandling.model;
   public class ErrorDetails
   {
      private String errorCode;
      private String errorMessage;
      private Throwable baseException;
      private Object otherExceptionDetails;
      /*
         get/set methods are also included
      */
   }

Using this approach, we can keep the information our handler requires in the exception. The benefits of this approach are:

  • Provides a standard interface
  • Provides extensibility

Therefore, our second problem of what information an exception should convey is solved. The developer, while designing his exception, decides what information is required by the handler.

How can we define exception handling behavior at runtime without modifying the application?

Now we arrive at the actual problem: When the developer wants an exception to be handled how does he or she specify what behavior is expected from the exception handler? The Patch framework does not expect the developer to specify the behavior desired from the exception handler.

The Patch framework assumes that an exception is defined or designed based on the problem the exception tries to convey. Ideally, for every problem type, a good designer creates a different exception type. This approach enables the client code catching the exception to understand the problem exactly, without going into the exception details.

If the exception type can convey the problem that has been encountered, then the exception type can also be used as a means to solve the problem. In other words, the exception type should also signify how the exception should be handled.

The Patch framework uses this principle as the basis for its design. It uses a config file in which the mapping between an exception type and its corresponding handler type is configured. The config file looks like this:

  <?xml version="1.0"?>
   <exceptions default=" com.patch.exception.handler.DefaultHandler">
      <exception class="com.patch.common.exception.PatchBaseException ">
         <handler class="com.patch.exception.handler.PatchExceptionHandler"/>
      </exception>
      <exception class="com.patch.common.exception.TransmitException ">
         <handler class="com.patch.exception.handler.TransmitExceptionHandler"/>
      </exception>
   </exceptions>

The root element of this XML file is the exceptions element. It has one attribute called default. This attribute contains the fully qualified class name of the exception handler used as the Patch framework’s default exception handler. If the Patch framework gets any exception of type PatchBaseExceptionIntf, for which an exception handler has not been defined in the config file, then this default handler is called to handle the exception.

The exceptions element has 0 or more exception child elements. The exception element has one attribute called class, which contains the fully qualified class name of the exception that must be handled. The exception element also contains one child element called handler, which contains an attribute called class. This attribute contains the fully qualified class name of the exception handler that should be called when an exception of the type defined in the class attribute of the parent exception element is sent to the Patch framework.

This config file is loaded once during the first call to the Patch framework. The framework stores all the mapping data in a HashMap instance. It does not create an exception handler object instance for every request. Since each of these handlers does not contain any state and just defines behavior, a single object instance is created and used by the Patch framework. Therefore, the handleException() method should be thread-safe.

The client code just needs to call the Patch framework with the exception as the parameter. At deployment, a handler can be written for the exception type and the mapping can be provided in the config file. The Patch framework calls the appropriate exception handler. This approach solves the following problem: How can we define exception handling behavior at runtime without modifying the application?

Example

To illustrate how the Patch framework works, let’s consider the try/catch block below. We catch an instance of PatchBaseException. This exception class extends java.lang.Exception and implements PatchBaseExceptionIntf. Therefore, this exception can be handled by the Patch framework.

 try
{
   //Some code here
}
catch(PatchBaseException e)
{
   ExceptionHandlerHelper.handleException(e);
}

Now let’s define two ExceptionHandler implementation classes. One of these would be configured as the default handler for the Patch framework:

 package com.patch.framework.exceptionhandling;
public class DefaultExceptionHandler implements ExceptionHandler
{
   public void handleException(PatchBaseExceptionIntf patchBaseException)
   {
      System.out.println("Default Handler");       
   }   
}

The other would be mapped to the PatchBaseException in the config XML file:

 package com.patch.framework.exceptionhandling;
public class PatchExceptionHandler implements ExceptionHandler
{
   public void handleException(PatchBaseExceptionIntf patchBaseException)
   {
      System.out.println("PatchException Handler");       
   }   
}

This is how our config file looks:

  <?xml version="1.0"?>
   <exceptions default=" com.patch.framework.exceptionhandling.DefaultExceptionHandler">
      <exception class="com.patch.common.exception.PatchBaseException ">
         <handler class="com.patch.framework.exceptionhandling.PatchExceptionHandler"/>
      </exception>
   </exceptions>

When a PatchBaseException occurs, the following output prints on the console: PatchException Handler.

However, if our config file looks like this:

  <?xml version="1.0"?>
   <exceptions default=" com.patch.framework.exceptionhandling.DefaultExceptionHandler">
      <exception class="com.patch.common.exception.PatchBaseRuntimeException">
         <handler class="com.patch.framework.exceptionhandling.PatchExceptionHandler"/>
      </exception>
   </exceptions>

Our output would be: Default Handler since no exception handler has been configured for PatchBaseException.

As you can see, this approach allows you to simply change a system’s behavior when an exception occurs without altering the system’s main business logic.

Developers just have to call the Patch framework’s handleException() method, which makes their job easier. Currently, the Patch framework comes bundled with just one exception handler. This handler sends an email notification about the exception.

Conclusion

In this article, we explored the Patch framework’s approach to handling exceptions. We explored the problems associated with exception handling and provided a remedy for each of those problems. Developers can extend the Patch framework for their usage and requirements.

Niranjan R. Kamath is working as a software developer in Hewlett Packard India. He has been working on Java-related technologies for the past two years and has four years of experience in software development. Currently, Kamath works in designing and developing solutions for telecommunication providers.