Data exception wrapping

Data can be stored in various ways, for example: If the storage method changes, then the low level Exception objects thrown by the data access layer can also change. For example, when the data store is moved from text files to a relational database, IOException is replaced with SQLException.

In order to prevent such a change from propagating to higher layers, one can wrap such low level Exceptions in a generic "data exception" wrapper, designed exclusively to protect the higher layers from such changes.

Throwable.getCause() can always be used to extract the original exception, if desired. (However, you sometimes need to be careful that both client and server know about the class of an underlying exception.)

Example

When building applications with the WEB4J tool, DAOException is the only checked exception that should be emitted by an application's data access layer. All occurrences of checked exceptions are either handled within the data layer, or, if they are thrown, translated into a DAOException.

In the following example, ConnectionSrc encapulates how an application creates or accesses Connections. When an SQLException or a NamingException occurs in its implementation, it is always wrapped in a DAOException, and then re-thrown to the caller.

This example is particulary appropriate because of the NamingException. If the NamingException was not translated into an DAOException, and thrown as is, then it would be visible directly to the caller. That is, the caller would know that the data layer is implemented using JNDI - which is likely inappropriate.

import java.util.*;
import java.util.logging.*;
import java.sql.Connection;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import hirondelle.web4j.database.DAOException;
import hirondelle.web4j.database.ConnectionSource;
import hirondelle.web4j.util.Util;

/** 
* Fetch a <code>Connection</code> from a pool managed by the container.
*/
public final class ConnectionSrc implements ConnectionSource  {

  //..elided
  
  private Connection getConnectionByName(String dbName) throws DAOException {
    Connection result = null;
    String dbConnString = (String)MAP_NAME_TO_CONN_STRING.get(dbName);  
    if(! Util.textHasContent(dbConnString)){
      throw new IllegalArgumentException("Unknown db name : " + Util.quote(dbName));
    }
    try {
      Context initialContext = new InitialContext();
      if (initialContext == null) {
        logger.severe("InitialContext is null. Db : " + Util.quote(dbConnString));
      }
      DataSource datasource = (DataSource)initialContext.lookup(dbConnString);
      if (datasource == null){
        logger.severe("Datasource is null for : " + dbConnString);
      }
      result = datasource.getConnection();
    }
    catch (NamingException ex){
      //EXCEPTION WRAPPING
      throw new DAOException(
        "Config error with JNDI and datasource, for db " + Util.quote(dbConnString), ex
      );
    }
    catch (SQLException ex){
      //EXCEPTION WRAPPING
      throw new DAOException(
       "Cannot get connection from datasource, for db " + Util.quote(dbConnString), ex
      );
    }
    return result;
  }
}
   

See Also :
Data access objects
Reduce database code duplication
Exception translation
A Web App Framework WEB4J
Beware of unknown root causes