Exceptions are designed to be thrown.  Although an Exception is an object like any other, Throwable, Exception and Error should always be thrown and never returned from a function call.

public Exception saveRecord(BaseModel record, long timeoutMsecs)
{
    try
    {
        :
    }
    catch (Exception e)  
    {
        logger.error(e);
        return e;
    }
    return null;  // no error
 }

In the above example, the developer has written some core framework code.  By throwing a generic Exception without doing some simple filtering, a great deal of work is proliferated throughout the code.  The correct way to deal with exceptions in this case is more complicated but only needs to be written once to save other developers a lot of time:

public void saveRecord(BaseModel record, long timeoutMsecs)
    throws RecordLockedException,  
           ConcurrentModificationException,  
           WriteException
{
    try
    {
        :
        if (record.getSequence() != remoteRecord.getSequence())
        {
            throw new ConcurrentModificationException(...);
        }
        :
    }
    catch (ConcurrentModificationException e)
    {
        logger.error(e);
        // since we created this exception
        // we know it is safe to rethrow
        throw e;
    }
    catch (DatabaseException e)
    {
        logger.error(e);
        if (e.getState() == STATE_LOCKED)
        {
            throw new RecordLockedException(e);
        }
        throw new WriteException(e);
    }
    catch (Exception e)     
    {
        // just in case we missed anything
        logger.error(e);
        throw new WriteException(e);
    }
}

The new method always returns void.  Any problems throw subclasses of DataAccessException, a runtime exception that doesn't need to be caught by every caller.  This improved method is useful because the caller can choose to catch only ConcurrentModificationException and deal with it specifically to workaround the problem.

blog comments powered by Disqus