An exception is an event that occurs during the execution of a program that disrupts the normal flow of the program and the program terminates abnormally.

Types of Exception – the exception can be either a checked exception or an unchecked exception.

A method may not handle exception thrown within it and would instead throw it up the method call stack to let its caller know that an abnormal event occurred. It does so by declaring that exception in the throws clause of its declaration.
For example,

class A {

    public Object doSomething(Object o) throws Exception{
        //...
    }

}
class B {
    
    public void doSomething() {
        try {
            Object param = ...
            A a = new A();
            Object o = a.doSomething(param);
            //...
        } catch(Exception e) {
            //do with the exception
        }
    }
    
}

So in the above, in class A method doSomething() throws Exception so class A throws Exception. The method doSomething() does not do anything with the exception, so when method doSomething() in class B calls method doSomething() of class A it has to either handle that exception using try{}catch{} block or it has to declare throws in the class declaration to throw exception to its caller.

The caller itself may handle the exception thrown by its callee or in turn propagate that to its own caller.

In the above example, The method doSomething() does not do anything with the exception, so when method doSomething() in class B calls method doSomething() of class A, it handles the exception in the catch{} block.

Or if again method doSomething() in class B does not want to handle exception but wants to throw exception to its caller then it has to do the following

class B {
    
    public void doSomething() throws Exception {
        A a = new A();
        Object o = a.doSomething();
    }
    
}

A method may translate an exception thrown by its callee into another exception and throw the new exception (to its own caller).

So if method doSomething() in class B wants to throw AppException() from its method then it has to wrap that exception to AppException() for throwing it by following way

public class AppException extends Exception {

  private static final long serialVersionUID = 1L;

  public AppException(Throwable t) {
    super(t);
  }

}
public class B {

  public void doSomething() throws AppException {
    try {
      Object param = ...
      A a = new A();
      Object o = a.doSomething(param);
      //...
    } catch (Exception e) {
      //translate the exception to AppException
      throw new AppException(e);
    }
  }

}

Exception Handling Best Practices

1. Handle Exception close to its origin

It does not mean “catch and swallow i.e. suppress or ignore exception”

Non-compliant – only exception message is logged

try {
    //...
} catch(AppException e) {
    Logger.info(e.getMessage());
}

Non-compliant – only exception object is logged

try {
    //...
} catch(AppException e) {
    Logger.info(e);
}

Non-compliant – only context message is logged

try {
    //...
} catch(AppException e) {
    Logger.info("context information");
}

It means “log and throw an exception relevant to the source layer”

Compliant – context message and exception object are present

try {
    //...
} catch (Exception e) {
    throw new AppException("context information", e);
}

2. Throw DaoException from DAO layer and AppException from Business layer.

3. In applications using Web Services, the Web Service (a.k.a Resource) layer,
– should catch all exceptions and handle them by creating proper error response and send it back to client.
– should not allow any exception (checked or unchecked) to be “thrown” to client.
– should handle the Business layer exception and all other unchecked exceptions separately.

Example

try {
    //...
} catch(AppException e) {
    // form error response using the exception’s error code and error message
} catch(Exception e) {
    // log the exception related message here, since this block is
    // expected to get only the unchecked exceptions
    // that had not been captured and logged elsewhere in the code.
    // form error response using the exception’s error code and/or error message
}

The catch handler for ‘Exception’ in the Web Service layer is expected to handle all unchecked exceptions thrown from within ‘try’ block

4. Log exception just once and log it close to its origin

Logging the same exception stack trace more than once can confuse the programmer examining the stack trace about the original source of exception.

try {
    //...
}    catch(AppException e) {
    // log the exception specific information
    // throw exception relevant to the source layer
}

5. When catching an exception and throwing it through an exception relevant to the source layer, make sure to use the construct that passes the original exception’s cause.

try{
    //...
} catch(SQLException e) {
    // log technical SQL Error messages, but do not pass
    // it to the client. Use user-friendly message instead
    Logger.error("An error occurred when searching for the product details" + e.getMessage());
    throw new DaoException("An error occurred when searching for the product details", e);
}

6. In case of existing code that may not have logged the exception details at its origin. In such cases, it would be required to log the exception details but do not overwrite the original exception’s message with some other message when logging.

Example

DAO Layer:

try{
    //...
} catch(SQLException e){
    //Logging missed here
    throw new DaoException("An error occurred when processing the query", e);
}

Since logging was missed in the exception handler of the DAO layer, it is mandatory in the exception handler of the next enclosing layer – in this example it is the (Business/Processor) layer.

Business/Processor Layer:

try{
    //...
} catch(DataAccessExceptione) {
    // logging is mandatory here as it was not logged
    // at its source (DAO layer)
    Logger.error(e.getMessage());
    throw new AppException(e.getMessage(), e);
}

7. Do not catch “Exception”

Accidentally swallowing RuntimeException

try{
    doSomething();
} catch(Exception e) {
    Logger.error(e.getMessage());
}

This code
– also captures any RuntimeException that might have been thrown by doSomething() method
– ignores unchecked exceptions and
– prevents them from being propagated

So, all checked exceptions should be caught and handled using appropriate catch handlers. And the exceptions should be logged and thrown to the outermost layer (i.e. the method at the top of the calling stack) using application specific custom exceptions relevant to the source layer.

8. Handle Exception before sending response to the Client

The layer of code component(i.e. the method at the top of the calling stack) that sends back response to the client, has to do the following:

– catch all checked exceptions and handle them by creating proper error response and send it back to client
– do not allow any checked exception to be thrown to the client
– handle the Business layer exception and all other checked exceptions raised from within the code in that layer separately
Examples of such components are:
– Service layer Classes in Web Service based applications
– Action Classes in Struts framework based applications

Example

try {
    //...
} catch(AppException e) {
    // create error response using the exception's error code and error message
}

Exceptional case 1 for handling Exception

There would be rare situations where users would prefer an easy to understand message to be displayed to them, instead of the system defined messages thrown by unrecoverable exceptions. In such cases, the method at the top of the calling stack, which is part of the code that sends response to the client is expected to handle all unchecked exceptions thrown from within the try block. By doing this, technical exception messages can be replaced with generic messages that the user can understand. So, a catch handler for Exception can be placed in it. This is an exception to best practice 6 and is only for the outermost layer. In other layers downstream in the layered architecture, catching Exception is not recommended for reasons explained under best practice 6.

Example

try {
    //...
} catch(AppException e) {
    // create error response using the exception's error code and error message
} catch(Exception e) {
    // Log the exception related message here, since this block is
    // expected to get only the unchecked exceptions
    // that had not been captured and logged elsewhere in the code,
    // provided the exception handling and logging are properly
    // handled at the other layers in the layered architecture.
    // create error response using the exception's error code and error message
}

Exceptional case 2 for handling Exception

Certain other exceptional cases justify when it is handy and required to catch generic Exception. These cases are very specific but important to large, failure-tolerant systems.
Consider a request processing system that reads requests from a queue of requests and processes them in order.

public void processAllRequests() {
    Request req= null;
    try {
        while(true) {
            req= getNextRequest();
            if(req!=null) {
                processRequest(req); // throws BadRequestException
            } else { // Request queue is empty, must be done
                break;
            }
        }
    } catch(BadRequestExceptione) {
        Logger.error("Invalid request:" + req, e);
    }
}

With the above code, if any exception occurs while the request is being processed (either a BadRequestException or any subclass of RuntimeException including NullPointerException), then that exception will be caught outside the processing while loop.
So, any error causes the processing loop to stop and any remaining requests will not be processed. That represents a poor way of handling an error during request processing.
A better way to handle request processing is to make two significant changes to the logic.
1) Move the try/catch block inside the request-processing loop. That way, any errors are caught and handled inside the processing loop, and they do not cause the loop to break.     Thus, the loop continues to process requests, even when a single request fails.
2) Change the try/catch block to catch a generic Exception, so any exception is caught inside the loop and requests continue to process.

public void processAllRequests() {
    while(true) {
        Request req= null;
        try {
            req= getNextRequest();
            if(req!=null) {
                processRequest(req); // throws BadRequestException
            } else { // Request queue is empty, must be done
                break;
            }
        } catch(BadRequestExceptione) {
            Logger.error("Invalid request:" + req, e);
        }
    }
}

In situations where requests, transactions or events are being processed in a loop, that loop needs to continue to process even when exceptions are thrown during processing.

9. Handling common Runtime Exception

NullPointerException

– It is the developer’s responsibility to ensure that no code can throw it
– Run null reference checks wherever it has been missed

NumberFormatException, ParseException

– Catch these and create new exceptions specific to the layer from which it is thrown (usually from business layer) using user-friendly and non technical messages
– To avoid ClassCastException, check the type of the class to be cast using the instanceof operator before casting.
– To avoid IndexOutOfBoundsException, check the length of the array before trying to work with an element of it.
– To avoid ArithmeticException, make sure that the divisor is not zero before computing the division.

Example

try{
    int item = Integer.parseInt(itemNumber);
} catch(NumberFormatException nfe) {
    Logger.error("Item number is invalid and not a number");
    throw new AppException("Item number is invalid and not a number", nfe);
}

All other unchecked exceptions i.e., RuntimeException, will be caught and handled by the Exception handler in the outermost or top layer.

Thanks for reading.

Tags:

I am a professional Web developer, Enterprise Application developer, Software Engineer and Blogger. Connect me on Roy Tutorials | TwitterFacebook Google PlusLinkedin | Reddit

Leave a Reply

Your email address will not be published. Required fields are marked *