Let's say you get
searchDivision as the
actionMethod from an HTTP request:
dispatchMethod will dispatch the request to a
searchDivision method in the derived
Action class of
BaseAppDispatchAction. Here, you can see that exception handling is done only in the base class, and the derived class just implements
Action methods. It confirms to the Template Method design pattern, where the exception-handling part remains invariant while the actual implementation (the varied part) of
dispatchMethod is deferred to the derived class.
The modified Struts
Action method mentioned earlier will look something like this after the above changes.
... String exceptionActionForward = "SearchAdjustmentPage"; String exceptionContext = "divisionAction.searchDivision"; ExceptionDisplayDTO expDTO = new ExceptionDisplayDTO(expActionForward, exceptionContext); expDisplayDetails.set(expDTO); ... DivisionDTO divisionDTO =divisionBusinessDelegate .getDivisionByNum(fromDivisionNum); ...
Wow! Now it looks clean. As exception handling is being done in one centralized place (
BaseAppDispatchAction), the scope of manual errors is also minimized.
However, we need to set the exception context and the name of the
ActionForward to which the requests will be forwarded if there's an exception. And we are setting this in a
Hmm. Fine. But why a
java.lang.ThreadLocal variable? The
expDisplayDetails is a protected data member in the
BaseAppDispatchActiion class, and that's why it needs to be thread-safe too. The
java.lang.ThreadLocal object comes to the rescue here.
We talked about an abstraction for handling exceptions in the last section. Here is the contract it should satisfy.
- Identify the type of exception and get the corresponding error code, which could be used to display a message to the end user.
- Log the exception. The underlying logging mechanism is hidden and could be configured based on some environmental properties.
As you might have noticed, the only exception we are catching in the presentation layer is
BaseAppException. As all checked exceptions are subclasses of
BaseAppException, implicitly, we are catching all of the derived classes of
BaseAppException. It's fairly easy to identify the error code based on the name of the class.
//exp is an object of BaseAppException String className = exp.getClass().getName();
Error codes can be configured in an XML file (named exceptioninfo.xml) based on the name of the exception class. Here is a sample of exception configuration.
<exception name="EmployeeConfirmationException"> <messagecode>messagecode.laborconfirmation</messagecode> <confirmationind>true</confirmationind> <loggingtype>nologging</loggingtype> </exception>
As you can see, we are making it fairly explicit that for this exception, the message code to be used is
messagecode.employeeconfirmation. The real message can then be extracted from a
ResourceBundle for internationalization purposes. We are also making it very clear that we don't need to perform logging for this type of exception, as it's just a confirmation message and not an application error.
Let's look at an example of a context-sensitive exception:
<exception name="RecordNotFoundException"> <messagecode>messagecode.recordnotfound</messagecode> <confirmationind>false</confirmationind> <contextind>true</contextind> <loggingtype>error</loggingtype> </exception>
true for this exception. The context you passed in
handleException method can be used to make a unique error code. For instance, if we passed
order.getOrder as a context, the resultant message code will be a concatenation of the exception's message code and the context passed. Hence, we get a unique message code like