Page 1 of 2 12 LastLast
Results 1 to 10 of 15

Thread: SWF 2.0 - Backtracking and exception catching

  1. #1
    Join Date
    May 2008
    Posts
    2

    Default SWF 2.0 - Backtracking and exception catching

    Hi,

    We have a problem with using back buttons in SWF 2.0. Backtracking inside one flow works fine until history=”invalidate/discard” attribute is used. When back button reach the invalid view the “SnapshotNotFound“ error occurs. Is it possible to catch this error and refresh the last valid page with useful message?

    With extending “AbstractFlowHandler” it is possible to handle that but only redirect is possible (request context is no longer available) but message could not be shown.

    PHP Code:
        @Override
        
    public String handleException(FlowException eHttpServletRequest request,
                
    HttpServletResponse response)
        {
            if (
    instanceof FlowExecutionRestorationFailureException)
            {
                if (
    e.getCause() instanceof SnapshotNotFoundException)
                {
                    return 
    getFlowId();
                }
            }
            return 
    super.handleException(erequestresponse);
        } 
    When customizing “SimpleMappingExceptionResolver” only catches the exception occurred during user event.

    PHP Code:
        <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
            <
    property name="exceptionMappings">
                <
    props>
                    <
    prop key="FlowExecutionRestorationFailureException">Login</prop>
                </
    props>
            </
    property>
        </
    bean
    Are there any other alternatives to catch errors which do not occur during user event?

    Thanks,
    Jaka, Tomi

  2. #2
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    Interesting proposed resolution step for this problem. So what you want to see is if they go back to an invalidated section of the flow and attempt to resume, a redirect to the most recent step with an error message for display. You'd definitely want to do a redirect there...

    I would try and do this with a FlowHandler like you are doing in your first step. Your challenge is going to be in determining the execution key of the most recent snapshot - that information isn't easily query-able currently.

    Would redirecting to a general error page with a button to "continue from most recent step" also be acceptable? In that case you'll have the same challenge--you'll need to know the flow id + key of the most recent flow execution (the execution id + snapshot id)

    Can you create a JIRA to track this and reference this thread?

    Keith
    Keith Donald
    Core Spring Development Team

  3. #3

    Default

    Hello Keith,

    We have the exact same requirement. I took the liberty to create a JIRA issue for it: SWF-727

    Regards,

    Duncan

  4. #4
    Join Date
    Mar 2008
    Posts
    170

    Default

    Would be a nice feature but I was now the first one who has voted for it.

    => push

    - Peter

  5. #5

    Default

    Is there any progress on this issue? We really would like to see this kind of functionality in WebFlow. Is there any chance that this will make it into the next version of WebFlow?

  6. #6
    Join Date
    Mar 2008
    Posts
    170

  7. #7

    Default

    15 now

    Javier

  8. #8
    Join Date
    Jun 2009
    Posts
    1

    Default SWF 2.0 - Backtracking and exception catching: a work around

    We have implemented the following workaround:

    We extended the "AbstractFlowHandler" as follows:
    PHP Code:
    public class ExceptionHandlingFlowHandler extends AbstractFlowHandler {
        
        @
    Override
        
    public String handleException(FlowException eHttpServletRequest requestHttpServletResponse response) {
            if (
    instanceof FlowExecutionRestorationFailureException) {
                try {
                    
    response.sendRedirect("next.jspx");
                } catch (
    IOException e1) {
                    
    // TODO Auto-generated catch block
                    
    e1.printStackTrace();
                }
                return 
    null;
            } else {
                return 
    super.handleException(erequestresponse);
            }
        }

    In WEB-INF we put next.jspx:
    PHP Code:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <
    html xmlns="http://www.w3.org/1999/xhtml"  xml:lang="en" lang="en">

        <
    head>

            <
    script type="text/javascript">
                function 
    forward(){
                    
    location.href="javascript: history.go(1)";
                }
            
    </script>

        </head>
        
        <body onload="setTimeout('forward()',0)"/>
        
    </html> 
    So when a user clicks the backbutton and the page the browser wants to send him to is invalid because of history=”invalidate/discard”, he is forwarded back to where he came from.

    Maybe not the prettiest way but for us it works fine

  9. #9
    Join Date
    Jun 2009
    Posts
    4

    Lightbulb

    Here's another possible solution, which does not involve redirection or JS.

    This one is a bit more general, since it will return the last valid FlowExecution for any invalid state id (in the same conversation, ofc). You may change this if you want, but it currently suits my needs.

    Here are teh codez :P

    Modified FlowExecutionRepository:
    Code:
    public class CtqFlowExecutionRepository extends DefaultFlowExecutionRepository {
    
        private Log log = LogFactory.getLog(CtqFlowExecutionRepository.class);
        
        private final static String LAST_VALID_STATE_KEY = "CtqFlowExecutionRepository_LAST_VALID_STATE_KEY";
    
        public CtqFlowExecutionRepository(DefaultFlowExecutionRepository lParent) {
            super(lParent.getConversationManager(), lParent.getSnapshotFactory());
        }
    
        /* (non-Javadoc)
         * @see org.springframework.webflow.execution.repository.impl.DefaultFlowExecutionRepository#getFlowExecution(org.springframework.webflow.execution.FlowExecutionKey)
         */
        @Override
        public FlowExecution getFlowExecution(FlowExecutionKey pKey) {
            log.trace("snapshot id: " + getSnapshotId(pKey));
            
            try {
                FlowExecution lFEx = super.getFlowExecution(pKey);
                Conversation lC = getConversation(pKey);
                lC.lock();
                try {
                    lC.putAttribute(LAST_VALID_STATE_KEY, getSnapshotId(pKey));
                } finally {
                    lC.unlock();
                }
                return lFEx;
            } catch (FlowExecutionRestorationFailureException e) {
                log.debug("FlowExecutionRestorationFailureException levée: l'état le plus récent sera retourné.");
                Conversation lC = getConversation(pKey);
                Serializable lSnapshotId = (Serializable) lC.getAttribute(LAST_VALID_STATE_KEY);
                log.debug(LAST_VALID_STATE_KEY + " = " + lSnapshotId);
                FlowExecutionSnapshot lSnapshot = getSnapshotGroup(lC).getSnapshot(lSnapshotId);
                return restoreFlowExecution(lSnapshot, pKey, lC);
            }
        }
    
        
    }
    Since we have to inject that into a FlowExecutor, here's the FactoryBean I used (SWF 2.0 does not let you inject a custom FlowExecutionRepository, as far as I know)
    Code:
    public class CtqFlowExecutorFactoryBean implements FactoryBean {
    
        private FlowExecutorImpl defaultFlowExecutor;
        private Object singleton;
    
        public Object getObject() throws Exception {
            if (defaultFlowExecutor == null) {
                throw new FactoryBeanNotInitializedException("defaultFlowExecutor ne peut être null");
            }
            
            if (singleton != null) { return singleton; }
            FlowExecutorImpl lFEx = defaultFlowExecutor;
            FlowDefinitionLocator lOriginalLocator = lFEx.getDefinitionLocator();
            FlowExecutionFactory lOriginalFactory = lFEx.getExecutionFactory();
            FlowExecutionRepository lCtqFER = 
                new CtqFlowExecutionRepository((DefaultFlowExecutionRepository)lFEx.getExecutionRepository());
            singleton = new FlowExecutorImpl(lOriginalLocator, lOriginalFactory, lCtqFER);
            return singleton;
            
        }
    
        public Class getObjectType() {
            return FlowExecutorImpl.class;
        }
    
        public boolean isSingleton() {
            return true;
        }
    
        /**
         * @param pDefaultFlowExecutor the defaultFlowExecutor to set
         */
        public void setDefaultFlowExecutor(FlowExecutorImpl pDefaultFlowExecutor) {
            defaultFlowExecutor = pDefaultFlowExecutor;
        }
        
    }
    Finally, XML snippets that glue all of this together:
    Code:
    <webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
    </webflow:flow-executor>
    
    <bean id="ctqFlowExecutor" class="[properclasspath].CtqFlowExecutorFactoryBean" >
    	<property name="defaultFlowExecutor" ref="flowExecutor" />
    </bean>
    
    
    <bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter" id="FHA">
        <property name="flowExecutor" ref="ctqFlowExecutor" />
    </bean>
    Tested quickly, and works. I'll post an update if I find a glitch with this.
    Last edited by angrysoul; Oct 6th, 2009 at 08:42 AM. Reason: Fixed FactoryBean, so it really returns a singleton

  10. #10
    Join Date
    Dec 2007
    Posts
    130

    Default

    I see this issue has not moved since a long time ago. I voted the issue in jira because I think it's a nice solution, but I don't find it completely satisfactory. I think that webflows should let you more options like:

    a) Go to last rendered state
    b) Navigate to a determinate state within the flow
    c) Go to an error page

    May be something like this (if possible):

    Code:
    <transition on="invalidated-history" to="someState">
    ...Options normally available to transition ...
    </tansition>
    Or if you think it is a mess, may be an specific label for invalidated history.

    In order to do that may be you should not delete invalidated snapshots, but mark them somehow as "invalid". If an snapshot is marked as invalid then execute the adequate transition. I will try to further develop the idea and will post again.

Posting Permissions

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