Page 1 of 3 123 LastLast
Results 1 to 10 of 27

Thread: Spring Faces best practice for error handling?

  1. #1
    Join Date
    May 2008
    Posts
    7

    Question Spring Faces best practice for error handling?

    What is the best practice for error handling when using spring faces?

    When we use plain JSF we call a service method in an action method on a managed bean which returns a String for the next view to render.

    When an exception is thrown in the service layer we handle the exception by adding a message to the facescontext and return null to remain on the same view.

    How can I do this (add a message and remain on the same view) with spring faces since the facescontext is not available in the flow definition.

  2. #2
    Join Date
    May 2008
    Posts
    227

    Default

    facesContext is a threadLocal variable and can be obtained any time through the request cycle by calling the appropriate ststic method

  3. #3
    Join Date
    Apr 2005
    Location
    San Francisco, CA
    Posts
    1,224

    Default

    Quote Originally Posted by fawzyj View Post
    facesContext is a threadLocal variable and can be obtained any time through the request cycle by calling the appropriate ststic method
    No actually, to reiterate, Gert is correct in stating that the FacesContext is unavailable during execution of the flow definition (i.e., during action execution).

    That said, the typical approaches to achieving your desired result, Gert, will require a bit of glue code. Essentially you just need to wrap the call to the service layer in another POJO that is able to properly handle the exception. In order to be able to record error messages, you will want to pass that method an instance of org.springframework.binding.message.MessageContext (which is available as an implicit "messageContext" EL variable in the flow definition) and have it return true or false to indicate whether the evaluation succeeds or fails.

    Then you would call that POJO as follows from your flow definition:
    Code:
    <transition on="foo" to="bar">
        <evaluate expression="myPojo.invokeMyService(messageContext)"/>
    </transition>
    In your POJO you can catch any exceptions, add errors to the MessageContext (which our implementation of FacesContext actually uses internally, thus the messages will show up in your JSF view) and return false to indicate that the transition should not continue. Alternately, you could use MultiAction instead of a POJO method evaluation in a similar manner.

    If you've got legacy JSF code that already does what you described and you want to integrate it with minimal change, you do have the option of leaving your action methods as-is and using Web Flow only to handle the navigation portion of handling the event.

    If you want to get creative and try and make things really generic and avoid the glue code so that you can continue to take advantage of the dynamic nature of the flow definition (this would certainly be my preference), you could consider applying an aspect to EvaluateAction.doExecute to catch the right exception types, grab the MessageContext and record errors, then return an error event to keep the transition from continuing. This could actually work quite well as long as what you are trying to catch are meaningful business exceptions where you could just pull out the exception message and record it as an error message.

    We realize that none of the glue code options are completely ideal (though the AOP approach could be quite nice, sometimes you can't make things that generic), and we want to make this sort of thing easier in the future. One idea we've got for the short term is to possibly incorporate dynamic scripting languages inline in your flow definition so you could have more complex logic like this that isn't possible in expression evaluation. Further down the road, we intend to implement flow builders in something more expressive than XML (such as Groovy, perhaps) so as to more naturally allow for such imperative concepts.
    Jeremy Grelle

    Staff Engineer, Web Products Team
    SpringSource

  4. #4
    Join Date
    May 2008
    Posts
    7

    Default

    Jeremy, thanks for your reply!

    We are now at the stage that we are going to implement the AOP approach. If we would apply an aspect to EvaluateAction.doExecute we have a few questions:
    - how do we apply the aspect since we do not declare the EvaluateAction bean ourselves
    - can you give us an example on how to return an error event to keep the transition from continuing

  5. #5
    Join Date
    Apr 2005
    Location
    San Francisco, CA
    Posts
    1,224

    Default

    Yes, it's a bit advanced which is why I didn't present AOP as the first option.

    In this case you would have to use AspectJ, instead of Spring's AOP proxies, either using load time weaving or compile time weaving (you would weave the aspect into the spring-webflow.jar in this case).

    As for returning a proper error event that will stop the transition, you can keep it as simple as:

    Code:
    return new Event(advisedEvaluateAction, "no", null);
    but I don't particularly like the idea of having the "no" string in there directly. A more future-proof way might be to reuse the ResultObjectBasedEventFactory that EvaluateAction uses indirectly. So in that case the code would look more like:

    Code:
    return new ResultObjectBasedEventFactory.createResultEvent(advisedEvaluateAction, Boolean.FALSE, requestContext);
    Jeremy Grelle

    Staff Engineer, Web Products Team
    SpringSource

  6. #6
    Join Date
    May 2008
    Posts
    7

    Default

    Would it be easier to use an exception-handler in the flow?
    Is it right that we then have to implement the FlowExecutionExceptionHandler and override the canHandle and handle method? And how can we stop transition from continuing in the handle method if an exception occurs (can you give an example)?

  7. #7
    Join Date
    Apr 2005
    Location
    San Francisco, CA
    Posts
    1,224

    Default

    Yes, actually, now that you mention it, a custom exception-handler would be the better solution. The trick with the exception-handler is that you have to generate your own response. I had forgotten how easy the changes to the API in 2.0 make this to achieve, else I would have recommended this in the first place.

    I wrote a quick prototype to test it out, and my FlowExecutionExceptionHandler implementation goes something like this:

    Code:
    @Component
    public class MyExceptionHandler implements FlowExecutionExceptionHandler {
    
        public boolean canHandle(FlowExecutionException ex) {
    	if (findBusinessException(ex) != null) {
    	    return true;
    	} else {
    	    return false;
    	}
        }
    
        public void handle(FlowExecutionException ex, RequestControlContext context) {
    	context.getMessageContext().addMessage(
    		new MessageBuilder().error().source(null).defaultText(findBusinessException(ex).getMessage()).build());
    
    	Object testState = context.getCurrentState();
    
    	if (testState instanceof ViewState) {
    	    ViewState viewState = (ViewState) testState;
    	    try {
    		viewState.getViewFactory().getView(context).render();
    	    } catch (IOException e) {
    		//Properly handle rendering errors here
    	    }
    	}
    
        }
    
        private MyBusinessException findBusinessException(FlowExecutionException ex) {
    	Throwable cause = ex.getCause();
    	while (cause != null) {
    	    if (cause instanceof MyBusinessException) {
    		return (MyBusinessException) cause;
    	    }
    	    cause = cause.getCause();
    	}
    	return null;
        }
    
    }
    This effectively causes the current view to re-render, and the added error message gets displayed by my <h:messages> component.

    This is a pretty nice approach as this can be registered on a flow level so that you can still use the <evaluate> actions and only need this one custom class to generically handle errors.
    Jeremy Grelle

    Staff Engineer, Web Products Team
    SpringSource

  8. #8
    Join Date
    May 2008
    Posts
    7

    Thumbs up

    Thanks a lot!!!

  9. #9
    Join Date
    Jun 2008
    Posts
    5

    Default blank page when resuming flow

    Hi there,

    we have used the solution Jeremy proposed (ie define a FlowExecutionExceptionHandler that adds the error messages and re-renders the current view). Re-rendering of the page seems to work, but when we try to perform an action (transition) after an error message was handled and the page was re-rendered, we get a blank page. Also no stack trace or anything is available.

    We have defined the FlowExecutionExceptionHandler in our webContext.xml file
    <bean id="webflowExceptionHandler" class="be.mypackage.bean.support.WebflowExceptionH andlerBean" >
    <property name="messageHandler" ref="messageHandler"/>
    </bean>

    and configured to use it in our flow.xml file as a general exception handler (apart from any view):
    <exception-handler bean="webflowExceptionHandler"/>

    Does anyone know why this is happening and/or what we can do about it?
    Last edited by Kristof; Jun 3rd, 2008 at 08:52 AM.

  10. #10
    Join Date
    Apr 2005
    Location
    San Francisco, CA
    Posts
    1,224

    Default

    Sorry about that, my solution was slightly flawed. Rendering from the FlowExecutionExceptionHandler like that will leave things in an inconsistent state...in particular, the FacesContext.renderResponse and FacesContext.responseComplete flags do not get properly reset since the example I showed executes render without redirecting (and we actually store those flags in flash scope). A simpler and better approach is to request a flowExecutionRedirect instead of rendering directly.

    This would change the handle method of my example to look like:

    Code:
    public void handle(FlowExecutionException ex, RequestControlContext context) {
        context.getMessageContext().addMessage(new MessageBuilder().error().source(null).defaultText(findBusinessException(ex).getMessage()).build());
    
        context.getExternalContext().requestFlowExecutionRedirect();
    }
    Jeremy Grelle

    Staff Engineer, Web Products Team
    SpringSource

Posting Permissions

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