Announcement Announcement Module
Collapse
No announcement yet.
SWF 2.0 - Backtracking and exception catching Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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
    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

    Comment


    • #3
      Hello Keith,

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

      Regards,

      Duncan

      Comment


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

        => push

        - Peter

        Comment


        • #5
          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?

          Comment


          • #6
            Oh now 8 votes

            http://jira.springframework.org/browse/SWF-727

            - Peter

            Comment


            • #7
              15 now

              Javier

              Comment


              • #8
                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

                Comment


                • #9
                  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, 08:42 AM. Reason: Fixed FactoryBean, so it really returns a singleton

                  Comment


                  • #10
                    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.

                    Comment


                    • #11
                      I would love to have this feature developed. Voted in JIRA for tracker https://jira.springsource.org/browse/SWF-727

                      Comment


                      • #12
                        That's smart! This can work for most purposes.

                        Comment


                        • #13
                          Hi Keith!

                          I'm using Spring Web Flow 2.3.0 and JSF 1.2.
                          Double-clicking on any button or hyperlink and back-navigating lead to snapshot not found exception.
                          In order to overcome it I edited max-execution-snapshots to -1 and made all the flow variables serializable and it worked.
                          But serializing flow variables is not an easy task thanks to non-serializable data members.
                          Could you please suggest how the exception handling method discussed here can be adopted as I'm using JSF.

                          Comment


                          • #14
                            We were experimenting with something along the same lines

                            Code:
                            <transition on-exception="org.springframework.webflow.execution.repository.FlowExecutionRestorationFailureException" to="${flowScope.previousViewState}" />
                            But it did not seem to work. Should this be possible in global transitions?

                            Comment


                            • #15
                              In the end we managed to use the Spring MVC exception handling to handle the problem. It seems to have en advantage over the previously suggested solution in that it does not require any specific java class to work

                              Code:
                                  <!-- Exception handling -->
                                  <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
                                      <property name="defaultErrorView" value="error/exception" />
                                      <property name="defaultStatusCode" value="500" />
                                      <property name="warnLogCategory" value="WARN" />
                                      <property name="exceptionMappings">
                                          <props>
                                              <prop key="org.springframework.webflow.execution.repository.FlowExecutionRestorationFailureException">error/backbutton</prop>
                                          </props>
                                      </property>
                                  </bean>
                              on the backbutton page we present the user with an error message and a javascript-link that takes them back to the previous page.
                              Last edited by Kent Damgaard; Jan 7th, 2013, 06:28 AM. Reason: Fixed code tag.

                              Comment

                              Working...
                              X