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 a 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.sql.Connection;
import java.sql.SQLException;
import java.util.logging.LogManager;
import java.util.logging.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

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

  /** 
   The caller needs to close the connection.
   @param dbConnString example <code>java:comp/env/jdbc/blahblahblah</code>.  
  */
  Connection getConnectionByName(String dbConnString) throws DAOException {
    Connection result = null;
    try {
      Context initialContext = new InitialContext();
      DataSource datasource = (DataSource)initialContext.lookup(dbConnString);
      if ( datasource == null ){
        logger.severe("Datasource is null for : " + dbConnString);
      }
      result = datasource.getConnection();
    }
    catch (NamingException ex){
      throw wrappedException("Config error. Connection string: " + dbConnString, ex);
    }
    catch (SQLException ex){
      throw wrappedException("Can't connect " + dbConnString, ex);
    }
    return result;
  }
  
  //..elided
  
  //PRIVATE

  /** 
   If desired, the caller can inspect the cause by calling Throwable.getCause().
   (That can be a problem sometimes if the runtime environment can't load the  
   class for the underlying Throwable.) 
  */
  private DAOException wrappedException(String message, Throwable ex) {
    DAOException result = new DAOException(message + " " + ex.getMessage());
    result.initCause(ex);
    return result;
  }
  
  private Logger logger = LogManager.getLogManager().getLogger(
    this.getClass().getPackageName()
  );
} 

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