Use chained exceptions

Many Java mavens recommend that business objects throw their own
exceptions. Internally, a component may catching a SQL or IO exception,
but what we really need to tell the user is that a data access error
occurred. Of course, at the same time, you do not want to sacrifice any
detail from the original exception. Retaining detail from the exception
can especially important in a layered, multi-tiered, or multi-platform
application.. The business component may not have direct access to the
log, and the exception is its only way of telling us what went wrong. 

In short, we need a way to keep the detail of the original message but
at the same time also add a user-friendly business exception. The former
may complain that it could not process a SQL query. The latter may
simply report that a "data access error" occurred and suggest that you
contact the database administration. We would also want to sure to log
both exceptions, to sure all the detail is maintain some actually
contact a DBA.

As the exception class was originally designed, doing something like
this is a problem. The Java exception mechanism allows you to throw only
throw one exception, not two or three. It's easy to "wrap" exceptions,
using one to initialize the next, but that results in loss of detail
over multiple layers. What we really need to do is "stack" or "chain"
the exceptions, so that each layer can add its own viewpoint to the
incident. Then, at the end, display them all, with the originating
exception at the bottom of the list. 

This approach works surprisingly well in a layered architecture. The
"topmost" layer is "closest" to the user, and so throws the most
"user-friendly" exceptions. The "lowest" layer throws the
"geek-friendly" errors that we need to solve the problem. When we chain
exceptions by linking them together, the user-friendly message comes
first, followed by the more detailed messages. The user is told what
they need to know first, and can leave the rest to the system
administrators. 

The best part is that chaining exceptions is very easy to implement in
Struts!

Java 1.4 provides new functionality for chaining exceptions, but it is
not difficult to write your own ChainedException class for using with
another JVM. The Scaffold package, currently bundled with Artimus
<http://husted.com/struts/>, includes a ChainedException class that
works with older JVMs.

Here's a try/catch block from a base Action that uses the scaffold
ChainedException class. It calls a member method to perform the business
operation and then analyzes the result. 

        // (1) 
       ActionErrors errors = new ActionErrors();
        try {
            executeLogic(mapping,form,request,response);
        }
        catch (Exception exception) {

        // (2) 
        servlet.log("*** ACTION EXCEPTION: ", exception);
        exception.printStackTrace();

            // (3) 
        errors.add(ActionErrors.GLOBAL_ERROR,
            new ActionError("error.general"));

            // (4) 
        StringBuffer sb = new StringBuffer();
        if (exception instanceof ChainedException) {
           ChainedException e = (ChainedException) exception;
           e.getMessage(sb);
        }
        else {
            sb.append(exception.getMessage());
        }
        }

         // (5)
        errors.add(ActionErrors.GLOBAL_ERROR,
            new ActionError("error.detail",sb.toString()));

        // (6) ... branch to error page if errors are not empty

(1) We setup an empty ActionErrors collection for later use, call the
business operation, and catch any and all exceptions it might throw. 

(2)  If an exception is thrown, we start by logging the message and
including a marker so that it is easier to find later. We also print the
stack trace, to sure that is recorded for future reference. 

(3)  We start by adding our own general error message to the errors
collection. This just says something like "The process did not complete.
Details should follow."

(4) If the business operation passes back a subclass of
ChainedException, we trundle through the chain, and collect all the
messages.

(5) To make the messages easy to display a presentation page, we wrap
them in a generic message template. The message template is a single
substitution code, 

        error.detail={0}

so that it just prints everything verbatim. 

The end result is a error messages like 

* The process did not complete. Details should follow.  
* A required resource is not available.  Cannot connect to MySQL server
localhost:3307. Is there a MySQL server running
the machine/port you are trying to connect to?
(java.net.ConnectException)  

being

1. The general error message, 
2. The business object message. 3. The original message from the JDBC
driver.

An advantage to this approach is that it provides a high-level message
for the user ("A required resource is not available.") and a low-level
message for technical support ("Cannot connect to MySQL server ...").
So, everybody's happy! The user gets a reasonable message. Support gets
the detail needed to solve the problem. 

HTH, Ted.

Struts Tips are released weekly the MVC-Programmers List. 
To subscribe, visit BaseBean Engineering <http://www.basebeans.com>. 

Archives of the past columns are available at JGuru 
<http://jguru.com/faq/subtopic.jsp?topicID=893704>

About Ted. Ted Husted is an active Struts Committer. He also 
moderates the Struts mailing list and the JGuru Struts FAQ.

Copyright Ted Husted 2002. All rights reserved. 

###
_______________________________________________
MVC-Programmers mailing list
[EMAIL PROTECTED]
http://www.basebeans.com:8081/mailman/listinfo/mvc-programmers

Reply via email to