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 Exception
s 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 Connection
s. 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; } }