Image of Exception handling strategy

ADVERTISEMENT

Table of Contents

Introduction

In this article we introduce a common strategy for handling exceptions in an OOP application, our strategy conforms to the best exception handling techniques and can be integrated in any application.

1. Overview

The following diagram provides an overview of our strategy, it shows the flow of the exception starting from the detection phase until reaching the handling phase. The diagram is read from bottom to top:

overview of strategy

  1. Error detection: At the bottom of the strategy is the error detection, this is where the exception occurs, it is either detected manually by the program or thrown by some external party call.
  2. Local exception handling: At the second level is the local exception handling, the class which detects an error tries to recover from it locally e.g. send the request to a backup server or wait X seconds and try again, etc… if the exception couldn’t be recovered, then it is propagated to the higher level.
  3. Propagate exception to higher levels: when local error handling doesn’t work, the class gathers all the information necessary to diagnose, reproduce and report the error, and then propagates the exception up the stack. If the detected exception is not low level dependent (dependent on low-level implementation) then it is thrown as is , otherwise it is translated to a custom exception in order to achieve decoupling between components.
  4. Keep propagating if nothing to do with the exception: the higher classes keep propagating the exception up the stack as long as they have nothing to do with it while closing any resources on the way up (like files, network connections, freeing allocated buffers etc.) and adding any contextual information which may be useful in determining the cause and severity of the error.
  5. Handle exception: at this stage, the exception reaches a class which is responsible for handling it, all the error information carried by the exception are logged here and based on the severity of the exception, the class either recovers from it or shut down the application gracefully.

2. Custom exception template

The first thing to do when implementing an exception handling strategy is to create a custom exception for each component of your application, the custom exception looks like the following:

public class ComponentException extends Exception {
    private static final long serialVersionUID = 1L;
    private int errorCode;
    private String errorDescription;
    private boolean isSevere;
 
    public ComponentException() {
        super();
    }
 
    public ComponentException(Exception ex) {
        super(ex);
    }
 
    public int getErrorCode() {
        return errorCode;
    }
 
    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }
 
    public String getErrorDescription() {
        return errorDescription;
    }
 
    public void setErrorDescription(String errorDescription) {
        this.errorDescription = errorDescription;
    }
 
    public boolean isSevere() {
        return isSevere;
    }
 
    public void setSevere(boolean isSevere) {
        this.isSevere = isSevere;
    }
 
}

The following describes the attributes of the ComponentException class:

  • errorCode: a unique code which identifies this error, the errorCode tells what went wrong, all the error codes of the application should be predefined in some static class. This attribute indicates to the exception catching code what to do with the error.
  • errorDescription: holds a description of the error with all the necessary details needed for users, application operators, and possibly the application developers, to understand what error occurred.
  • isSevere: indicates whether the error is severe or not, this attribute is updated as the exception traverse up the stack based on the context of the error, the severity indicates to the exception catching code whether to halt the application or keep processing.

3. Throwing exception

After detecting an error and failing to recover from it, the exception is propagated up the call stack until reaching some try-catch block which handles it. The exception can be either propagated as is or translated to a custom exception.

3.1 Throwing exception as is

If the exception doesn’t depend on low level implementation or dynamic implementation which is regularly changed, then you just close the opened resources and let the exception pass the call stack without catching it. Here is an example:

public void doSomething() throws SomeException {
    try{
        doSomethingThatCanThrowException();
    } finally {
       //close the opened resources
    }
}

3.2 Throwing custom exception

When the caught exception depends on low level or dynamic implementations, it is translated to a component specific exception and then rethrown up the call stack. Here is an example:

public Student readStudent(String id) throws SomeException {
        try
        {
            // Some code which reads a student from oracle database 
        }
        catch(SQLException ex)
        {
            DataAccessException dataAccessException = new DataAccessException(ex);
            dataAccessException.setErrorCode(101); // we assume this code denotes student not found
            dataAccessException.setErrorMessage("An error occurred while reading " + 
            "student with id: " + id + " from database");
            dataAccessException.setSeverity(false);
            throw dataAccessException;
        }
}

The exception keeps propagating as long as it doesn’t reach a responsible class which is able to handle it.

P.S: it is highly recommended that the severity of the exception is updated as the exception propagates up the stack, regardless if the exception is thrown as is or translated to a custom exception.

4. Catching Exceptions

Somewhere in your application you need to catch and react to thrown exceptions, typically you will do this towards the top of the call hierarchy.

The first thing to do when catching an exception is to log it, normally i prefer using printStackTrace(), then the handling process depends on the severity of the exception:

  • If exception is severe, then the developers and the application operators are notified and the application shut down gracefully.
  • If exception is not severe then the handling process is done according to the error code. Normally there are 2 possibilities, either recover from exception silently or notify end users and stop the current process.

Here is an example:

try{
    startTheWholeThing();
} catch(MyAppException e) {
    e.printStackTrace();
    if(e.isSevere())
    {
        notifyNonUsers(e);
        // Halt the system gracefully        
    }
    else
    {
        if(e.getErrorCode() == 100)
        {
            // do some silent recovery logic
        }
        else
        {
            notifyUsers(e);
        }
    }
}

That’s the strategy that I follow for handling exceptions in my applications, I hope you liked it. Don’t hesitate to share with me your opinions in the comments section below.

Summary

In this article we introduce a common strategy for handling exceptions in an OOP application, our strategy conforms to the best exception handling techniques and can be integrated in any application.

Next Steps

If you're interested in learning more about the basics of Java, coding, and software development, check out our Coding Essentials Guidebook for Developers, where we cover the essential languages, concepts, and tools that you'll need to become a professional developer.

Thanks and happy coding! We hope you enjoyed this article. If you have any questions or comments, feel free to reach out to jacob@initialcommit.io.

Final Notes