Results 1 to 10 of 10

Thread: <exception-handler> in SWF1.0.5 drops stack trace of original exception???

Threaded View

  1. #1
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default <exception-handler> in SWF1.0.5 drops stack trace of original exception???

    I am using SWF 1.0.5 on a large critical project that is near completion and upgrading to 2.0 is, unfortunately, not feasible (I wish it were backwards-compatible.) I have implemented a very well-working error-handling strategy (almost!) using SWF's <exception-handler>. Each of my flows has the following definition:

    Code:
    <exception-handler bean="flowErrorHandler"/>
    The "flowErrorHandler" bean is defined as follows:

    Code:
    <bean id="flowErrorHandler" class="com.xyz.myapp.web.webflow.FlowExceptionHandler">
            <property name="targetStates">
                <map>
                    <entry key="com.xyz.myapp.web.BusinessException1" value="targetState1"/>
                    <entry key="com.xyz.myapp.web.BackNavigationException" value="expiredMsgState" />
                    <entry key="java.lang.Throwable" value="systemerror"/>
                </map>
            </property>
        </bean>
    My FlowExceptionHandler extends TransitionExecutingStateExceptionHandler adds the ability to inject the target states via a setter and some custom processing:

    Code:
    import com.xyz.common.config.InvalidConfigurationException;
    import org.apache.log4j.Logger;
    import org.springframework.webflow.engine.RequestControlContext;
    import org.springframework.webflow.engine.support.TransitionExecutingStateExceptionHandler;
    import org.springframework.webflow.execution.FlowExecutionException;
    import org.springframework.webflow.execution.ViewSelection;
    
    import java.util.Map;
    import java.text.MessageFormat;
    
    public class FlowExceptionHandler extends TransitionExecutingStateExceptionHandler {
        private static final String MSG_ERR_EXCEPTION_CLASS =
                "Error configuring FlowExceptionHandler: ''{0}'' is not a recognized exception class.";
        private static final String MSG_REGISTERING_HANDLER =
                "Registered error handler for ''{0}'': on error will forward to stateId=''{1}''.";
        private static final String MSG_ERROR_INFO =
                "{0} was caught and handled by global flow error handler in flow ''{1}'', state ''{2}''. Proceeding to designated state ''{3}''.";
        protected static final Logger logger = Logger.getLogger(FlowExceptionHandler.class);
    
        /**
         * Handles the incoming flow exception and redirects the flow to the designated state associated with this exception
         * type. The exception-type-to-state mappings must be loaded via the {@link #setTargetStates} method.
         *
         * @param e       the FlowExecutionException object generated by the WF engine that wraps the original exception
         * @param context context of the current client request
         * @return signaled event to proceed to the designated view that is determined by the configuration of this handler
         */
        @Override
        public ViewSelection handle(FlowExecutionException e, RequestControlContext context) {
            logger.error("Some message", e); // want to log the complete stack trace!!!
            
            // do custom processing here...
            return super.handle(e, context);
        }
    
        /**
         * Checks whether this handler should handle the incoming exception. Overrides the default behavior on the SWF
         * (v1.0.5) framework's class to avoid the infinite loop in the case of the {@link #handle} method
         * throwing an IllegalArgumentException due to a handler being incorrectly configured to forward to a non-existing
         * state as well as to handle any Throwable or Exception.
         *
         * @param e the FlowExecutionException object generated by the WF engine that wraps the original exception
         * @return boolean true or false.
         */
        @Override
        public boolean handles(FlowExecutionException e) {
            return e.getCause() != null && !(e.getCause() instanceof IllegalArgumentException) && super.handles(e);
        }
    
        /**
         * Sets the mappings between the exception types (class names) and target flow states to which control must be
         * directed after the exception is handled. This <tt>set</tt> method is introduced to allow <i>setter-based
         * injection</i> of the mappings into this handler, and internally uses the {@link #add} method on the superclass to
         * insert the mappings.
         *
         * @param states map of key/value pairs where each key is an exception class name and each value is a flow state ID
         * @throws InvalidConfigurationException if invalid exception to state configuration is being loaded, e.g. one of
         *                                       the specified exception classes is not recognized, etc.
         */
        public void setTargetStates(Map<String, String> states) throws InvalidConfigurationException {
            for (String className : states.keySet()) {
                try {
                    String stateId = states.get(className);
                    add(Class.forName(className), stateId);
                    logger.debug(MessageFormat.format(MSG_REGISTERING_HANDLER, className, stateId));
                }
                catch (ClassNotFoundException ex) {
                    throw new InvalidConfigurationException(MessageFormat.format(MSG_ERR_EXCEPTION_CLASS, className), ex);
                }
            }
        }
    }
    All my exceptions within the application are runtime exceptions, of course, which allows them to freely propagate to the designated handlers without being altered or mishandled. Everything works beautifully, except for one thing: the FlowExecutionException object created by the webflow engine does not have the stack trace of the cause, i.e the actual application exception. Was this done on purpose? I doubt that such a thing (chopping off the stack trace) could be deliberately done by such expert programmers as the WF authors. No good framework developer will ever assume that they could replace - not complement! - the original complete history of an application-specific error with their own generic one-liner? I see bad programmers do that every day, but not the Spring guys! What am I missing? Must be something obvious.

    If I disable the <exception-handler> thing and let the exceptions propagate to my global Spring MVC exception resolver, everything is fine, the stack trace is not lost. But using <exception-handler> gives me FlowExecutionException that just doesn't contain the complete error information, which is unbelievable, and unacceptable to me. If it's a bug, I am sure it is fixed in 2.0, but is there a way to make it work in 1.0.5? I would really love to make it work b/c it is such a slick way to implement a one-stop error-handling solution.

    Thanks for any advice. Please help!!!
    Last edited by constv; May 24th, 2008 at 09:50 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •