Announcement Announcement Module
Collapse
No announcement yet.
handling end states Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    Feature proposal: SpringWebFlowExceptionResolver (part 2)

    And the code...

    FYI: for anyone reading this post now (i.e., after July 26, 2006), you should refer to the code in the JIRA issue for the "latest and greatest".

    Code:
    package com.xyz.spring.web.servlet.handler;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.lang.ClassUtils;
    import org.apache.commons.lang.StringUtils;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
    import org.springframework.webflow.execution.repository.NoSuchFlowExecutionException;
    import org.springframework.webflow.execution.repository.conversation.NoSuchConversationException;
    import org.springframework.webflow.executor.support.RequestPathFlowExecutorArgumentExtractor;
    
    /**
     * SpringWebFlowExceptionResolver is a custom extension of
     * {@link org.springframework.web.servlet.handler.SimpleMappingExceptionResolver SimpleMappingExceptionResolver}
     * which resolves Spring Web Flow (SWF) exceptions by redirecting to the
     * originating flow URL to launch a new flow. The list of exceptions that should
     * be resolved is configurable and therefore not limited to exceptions from the
     * SWF framework.
     * 
     * <p>
     * Special note: the current implementation is tightly coupled to path
     * construction semantics of
     * {@link org.springframework.webflow.executor.support.RequestPathFlowExecutorArgumentExtractor RequestPathFlowExecutorArgumentExtractor}.
     * Thus, if you need to use a SpringWebFlowExceptionResolver to parse and/or
     * redirect to flow request URIs that are not dependent on
     * RequestPathFlowExecutorArgumentExtractor, you will need to override
     * {@link #resolveException(HttpServletRequest, HttpServletResponse, Object, Exception)}
     * and provide a different parsing and redirecting strategy.
     * </p>
     * 
     * <p>
     * In addition, if an exception is encountered which is not determined to be a
     * supported SWF exception, SpringWebFlowExceptionResolver will simply delegate
     * to the behavior of SimpleMappingExceptionResolver. In this manner, a
     * SpringWebFlowExceptionResolver can be used as a drop-in replacement for a
     * SimpleMappingExceptionResolver.
     * </p>
     * 
     * <p>
     * By default, the following SWF exceptions are supported. These can, however,
     * be overridden by calling {@link #setSupportedSwfExceptions(Class[])}.
     * </p>
     * 
     * <ul>
     * <li>{@link org.springframework.webflow.execution.repository.conversation.NoSuchConversationException NoSuchConversationException}</li>
     * <li>{@link org.springframework.webflow.execution.repository.NoSuchFlowExecutionException NoSuchFlowExecutionException}</li>
     * </ul>
     * 
     * <p>
     * This code was originally inspired by <a
     * href="http://forum.springframework.org/showthread.php?t=25649"
     * target="_blank">a post on the SWF forum by Rob Moore</a>.
     * </p>
     * 
     * @see #resolveException(HttpServletRequest, HttpServletResponse, Object,
     *      Exception)
     * @see org.springframework.web.servlet.handler.SimpleMappingExceptionResolver
     * 
     * @author <a href="http://sambrannen.com" title="Sam Brannen">Sam Brannen</a>
     * @version $Revision: 1.3 $
     */
    public class SpringWebFlowExceptionResolver extends SimpleMappingExceptionResolver {
    
        // ------------------------------------------------------------------------|
        // --- CONSTANTS ----------------------------------------------------------|
        // ------------------------------------------------------------------------|
    
        /**
         * @see RequestPathFlowExecutorArgumentExtractor#createFlowExecutionUrl(java.lang.String,
         *      org.springframework.webflow.FlowExecutionContext,
         *      org.springframework.webflow.ExternalContext)
         */
        private static final String   CONVERSATION_ID_PREFIX    = "/_c";
    
        /**
         * @see RequestPathFlowExecutorArgumentExtractor#createFlowExecutionUrl(java.lang.String,
         *      org.springframework.webflow.FlowExecutionContext,
         *      org.springframework.webflow.ExternalContext)
         */
        private static final String   FLOW_EXECUTION_KEY_PREFIX = "/_flowExecutionKey";
    
        /** Class Logger. */
        private static final Log      LOG                       = LogFactory.getLog(SpringWebFlowExceptionResolver.class);
    
        // ------------------------------------------------------------------------|
        // --- INSTANCE VARIABLES -------------------------------------------------|
        // ------------------------------------------------------------------------|
    
        private List<Class>           supportedSwfExceptions    = new ArrayList<Class>();
    
        // ------------------------------------------------------------------------|
        // --- CONSTRUCTORS -------------------------------------------------------|
        // ------------------------------------------------------------------------|
    
        /**
         * Sets default supported SWF exception classes.
         * 
         * @see #setSupportedSwfExceptions(Class[])
         */
        public SpringWebFlowExceptionResolver() {
    
            getSupportedSwfExceptions().add(NoSuchConversationException.class);
            getSupportedSwfExceptions().add(NoSuchFlowExecutionException.class);
        }
    
        // ------------------------------------------------------------------------|
        // --- INSTANCE METHODS ---------------------------------------------------|
        // ------------------------------------------------------------------------|
    
        /**
         * @return Returns the supportedSwfExceptions.
         */
        protected final List<Class> getSupportedSwfExceptions() {
    
            return this.supportedSwfExceptions;
        }
    
        // ------------------------------------------------------------------------|
    
        /**
         * Determines if the supplied <code>exceptionType</code> is supported by
         * this SpringWebFlowExceptionResolver for SWF exception resolution.
         * 
         * @param exceptionType
         *            The class of the exception against which to test for support.
         * 
         * @return <code>true</code> if the type of exception is supported.
         */
        @SuppressWarnings("unchecked")
        protected boolean isSupportedSwfException(final Class exceptionType) {
    
            for (Class clazz : getSupportedSwfExceptions()) {
                if (clazz.isAssignableFrom(exceptionType)) {
                    return true;
                }
            }
    
            return false;
        }
    
        // ------------------------------------------------------------------------|
    
        /**
         * Overrides resolveException(): if the supplied exception is
         * <em>supported</em> as an SWF exception by this exception resolver, the
         * returned ModelAndView will redirect to the original request URI but with
         * the continuation key and/or flowExecutionKey removed (i.e., to launch a
         * new flow); otherwise, this method delegates to the parent implementation
         * for handling exception resolution.
         * 
         * @see #isSupportedSwfException(Class)
         * @see org.springframework.web.servlet.HandlerExceptionResolver#resolveException(javax.servlet.http.HttpServletRequest,
         *      javax.servlet.http.HttpServletResponse, java.lang.Object,
         *      java.lang.Exception)
         */
        @Override
        public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
                Exception ex) {
    
            if (LOG.isDebugEnabled()) {
                LOG.debug("resolveException() called for exception [" + ex + "] and handler [" + handler + "].");
            }
    
            ModelAndView modelAndView;
    
            if (isSupportedSwfException(ex.getClass())) {
    
                String requestURI = request.getRequestURI();
    
                // example (SWF 1.0):
                // http://localhost:8080/registration/_flowExecutionKey/_c60C9DD91-811F-73AF-EFFC-D530F5A567DF_kD8A8C4BF-DEA4-5A62-07E9-833244486074
                if (requestURI.contains(FLOW_EXECUTION_KEY_PREFIX)) {
                    requestURI = StringUtils.substringBefore(requestURI, FLOW_EXECUTION_KEY_PREFIX);
                }
    
                // example (SWF 1.0 EA):
                // http://localhost:8080/registration/_cC34B259D-41C3-84CA-9280-CD020605E3BA
                //
                if (requestURI.contains(CONVERSATION_ID_PREFIX)) {
                    requestURI = StringUtils.substringBefore(requestURI, CONVERSATION_ID_PREFIX);
                }
    
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Encountered exception in SWF [" + ClassUtils.getShortClassName(ex.getClass())
                            + "]. Redirecting to [" + requestURI + "].");
                }
    
                modelAndView = getModelAndView("redirect:" + requestURI, ex, request);
    
            } else
                modelAndView = super.resolveException(request, response, handler, ex);
    
            return modelAndView;
        }
    
        // ------------------------------------------------------------------------|
    
        /**
         * @param supportedSwfExceptions
         *            The supportedSwfExceptions to set.
         */
        public final void setSupportedSwfExceptions(final List<Class> supportedSwfExceptions) {
    
            this.supportedSwfExceptions = supportedSwfExceptions;
        }
    
        // ------------------------------------------------------------------------|
    
    } // class SpringWebFlowExceptionResolver
    Last edited by Sam Brannen; Jul 26th, 2006, 08:19 PM. Reason: updated regarding the new JIRA issue

    Comment


    • #17
      Yes Sam, JIRA! :-)

      Thanks!

      Keith

      Comment


      • #18
        Opened JIRA issue for SpringWebFlowExceptionResolver

        Hi Keith,

        Yes Sam, JIRA! :-)
        http://opensource.atlassian.com/proj...browse/SWF-158

        Thanks!
        You're welcome!

        - Sam

        Comment


        • #19
          I apply SpringWebFlowExceptionResolver class in my project.
          But when exception appear (eg. NoSuchFlowExecutionException) I have "The requested resource () is not available" message and following text in my log:
          2006-08-08 19:25:19,796 DEBUG [org.springframework.webflow.executor.support.Sprin gWebFlowExceptionResolver] - <Processed SWF exception [NoSuchFlowExecutionException]. Redirecting to [/foodtogo/placeOrder.htm].>
          2006-08-08 19:25:19,828 WARN [org.springframework.web.servlet.PageNotFound] - <No mapping for [/foodtogo/foodtogo/placeOrder.htm] in DispatcherServlet with name 'foodtogods'>
          In foodtogo-servlet.xml I have following bean declared:
          <bean name="/placeOrder.htm" class="org.springframework.webflow.executor.mvc.Fl owController">
          <property name="flowExecutor" ref="flowExecutor"/>
          <property name="cacheSeconds" value="5"/>
          </bean>
          I think, that prefix foodtogo is redundant, but what should I do?

          Witek.

          Comment


          • #20
            Hi Witek,

            Originally posted by witalis3
            I apply SpringWebFlowExceptionResolver class in my project.
            But when exception appear (eg. NoSuchFlowExecutionException) I have "The requested resource () is not available" message and following text in my log:
            2006-08-08 19:25:19,796 DEBUG [org.springframework.webflow.executor.support.Sprin gWebFlowExceptionResolver] - <Processed SWF exception [NoSuchFlowExecutionException]. Redirecting to [/foodtogo/placeOrder.htm].>
            2006-08-08 19:25:19,828 WARN [org.springframework.web.servlet.PageNotFound] - <No mapping for [/foodtogo/foodtogo/placeOrder.htm] in DispatcherServlet with name 'foodtogods'>
            In foodtogo-servlet.xml I have following bean declared:
            <bean name="/placeOrder.htm" class="org.springframework.webflow.executor.mvc.Fl owController">
            <property name="flowExecutor" ref="flowExecutor"/>
            <property name="cacheSeconds" value="5"/>
            </bean>
            I think, that prefix foodtogo is redundant, but what should I do?

            Witek.
            As I mentioned in the JavaDoc, the current implementation is coupled to request path construction semantics of RequestPathFlowExecutorArgumentExtractor. Have a look at the code:

            Code:
            /**
             * Resolves an SWF exception by returning a ModelAndView that will redirect
             * to the original request URI but with the continuation key and/or
             * flowExecutionKey either modified or removed in order to launch a new
             * flow.
             * 
             * <p>
             * Implementation guidelines:
             * </p>
             * 
             * <ul>
             * <li>parse the request URI from the provided <code>request</code>.</li>
             * <li>determine how to construct the ModelAndView (i.e., most likely a
             * <em>redirect</em>) based on the parsed request URI and the provided
             * <code>handler</code> and <code>exception</code>.</li>
             * <li>construct and return an appropriate ModelAndView that will launch a
             * new flow.</li>
             * </ul>
             * 
             * <p>
             * Note: this default implementation is somewhat coupled to request path
             * construction semantics of
             * {@link RequestPathFlowExecutorArgumentExtractor}. Thus, if you need to
             * use a SpringWebFlowExceptionResolver to parse and/or redirect to flow
             * request URIs that require a different parsing and redirecting strategy,
             * you will need to override this method in a subclass.
             * </p>
             * 
             * @param request
             *            The current HTTP request
             * @param response
             *            The current HTTP response
             * @param handler
             *            The executed handler, or <code>null</code> if none chosen at
             *            the time of the exception (for example, if multipart
             *            resolution failed)
             * @param exception
             *            The exception that got thrown during handler execution.
             * 
             * @return A ModelAndView which will launch a new flow.
             */
            protected ModelAndView resolveSwfException(final HttpServletRequest request, final HttpServletResponse response,
                    final Object handler, final Exception exception) {
            
                String requestURI = request.getRequestURI();
            
                // example (SWF 1.0):
                // http://localhost:8080/registration/_flowExecutionKey/_c60C9DD91-811F-73AF-EFFC-D530F5A567DF_kD8A8C4BF-DEA4-5A62-07E9-833244486074
                if (requestURI.contains(FLOW_EXECUTION_KEY_PREFIX)) {
                    requestURI = StringUtils.substringBefore(requestURI, FLOW_EXECUTION_KEY_PREFIX);
                }
            
                // example (SWF 1.0 EA):
                // http://localhost:8080/registration/_cC34B259D-41C3-84CA-9280-CD020605E3BA
                if (requestURI.contains(CONVERSATION_ID_PREFIX)) {
                    requestURI = StringUtils.substringBefore(requestURI, CONVERSATION_ID_PREFIX);
                }
            
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Processed SWF exception [" + ClassUtils.getShortClassName(exception.getClass())
                            + "]. Redirecting to [" + requestURI + "].");
                }
            
                return getModelAndView("redirect:" + requestURI, exception, request);
            }
            Thus, to solve your problem, you'll need to override resolveSwfException() with an implementation that constructs the redirect URL correctly for web flows that do not use REST-ful URLs (i.e., ones that do not use RequestPathFlowExecutorArgumentExtractor).

            As I mentioned originally, this default implementation is only a start. If/when Keith incorporates it into the core SWF code, it will need to support different strategies for the varying repository implementations available, etc.

            In the meantime, if you come up with a working solution, I am sure the community would appreciate hearing about it and seeing your code.

            I am rather busy on a project at the moment, but if I find some free time in the coming weeks, I will try to code up some other strategies as well.

            Hope this helps get you on the right path.

            regards,

            Sam

            Comment


            • #21
              This is my code:
              Code:
              /*
               * Copyright 2002-2006 the original author or authors.
               *
               * Licensed under the Apache License, Version 2.0 (the "License");
               * you may not use this file except in compliance with the License.
               * You may obtain a copy of the License at
               *
               *      http://www.apache.org/licenses/LICENSE-2.0
               *
               * Unless required by applicable law or agreed to in writing, software
               * distributed under the License is distributed on an "AS IS" BASIS,
               * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               * See the License for the specific language governing permissions and
               * limitations under the License.
               */
              /**
               * created 2006-08-09
               */
              package net.chrisrichardson.foodToGo.webflow;
              
              import javax.servlet.http.HttpServletRequest;
              import javax.servlet.http.HttpServletResponse;
              
              import org.apache.commons.lang.ClassUtils;
              import org.springframework.web.servlet.ModelAndView;
              import org.springframework.webflow.ExternalContext;
              import org.springframework.webflow.context.servlet.ServletExternalContext;
              import org.springframework.webflow.executor.mvc.FlowController;
              import org.springframework.webflow.executor.support.FlowExecutorArgumentExtractor;
              import org.springframework.webflow.executor.support.SpringWebFlowExceptionResolver;
              
              /**
               * FoodToGoSWFExceptionResolver is a custom extension of {@link SpringWebFlowExceptionResolver}
               * @see org.springframework.webflow.executor.support.SpringWebFlowExceptionResolver
               * @see org.springframework.web.servlet.handler.SimpleMappingExceptionResolver
               * @author <a href="http://www.witalis.neostrada.pl/foodtogo.html" title="Food To Go sample application">Witold Buszkiewicz</a>
               *
               */
              public class FoodToGoSWFExceptionResolver extends
              		SpringWebFlowExceptionResolver {
              	private FlowController flowController;
              	@Override
              	/**
              	 * @return A ModelAndView which will launch a new flow, when current flow is active. When flow is ended default flow will launch 
              	 * (if FlowController defaultFlowId is set) or defaultErrorView will display 
              	 */
              	protected ModelAndView resolveSwfException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) {
                      String servletPath = request.getServletPath();
                      if (LOG.isDebugEnabled()) {
                          LOG.debug("servletPath [" + servletPath + "].");
                      }
                      FlowExecutorArgumentExtractor feae = flowController.getArgumentExtractor();
                      String flowIdParameterName = feae.getFlowIdParameterName();
                      ExternalContext externalContext = new ServletExternalContext(null, request, response); 
                      String flowId = feae.extractFlowId(externalContext);
                      servletPath = servletPath + "?" + flowIdParameterName + "=" + flowId;
                      if (LOG.isDebugEnabled()) {
                          LOG.debug("Processed SWF exception [" + ClassUtils.getShortClassName(exception.getClass())
                                  + "]. Redirecting to [" + servletPath + "].");
                      }
              
                      return getModelAndView("redirect:" + servletPath, exception, request);
              	}
              	/**
              	 * @param flowController the flowController to set
              	 */
              	public void setFlowController(FlowController flowController) {
              		this.flowController = flowController;
              	}
              }
              and portion of my context:
              Code:
              	<bean name="/flowController.htm" class="org.springframework.webflow.executor.mvc.FlowController">
              		<property name="flowExecutor" ref="flowExecutor"/>
              		<property name="cacheSeconds" value="5"/>
              		<property name="defaultFlowId" value="placeOrder-flow"/>
              	</bean>
              	<bean id="globalExceptionResolver" class="net.chrisrichardson.foodToGo.webflow.FoodToGoSWFExceptionResolver">
              		<property name="supportedSwfExceptions">
              			<list>
              				<value>org.springframework.webflow.execution.repository.NoSuchFlowExecutionException</value>
              			</list>
              		</property>
              		<property name="defaultErrorView" value="index" />
              		<property name="flowController" ref="/flowController.htm"></property>
              	</bean>
              I now, that it is very poor solution, but working for me.

              regards

              Witek.

              Comment


              • #22
                Hi Witek,

                Originally posted by witalis3
                This is my code:
                Thanks for posting it. I'm sure others will find it useful. In fact, you might consider adding it to the Jira issue (see earlier post in this thread).

                Originally posted by witalis3
                I now, that it is very poor solution, but working for me.
                I don't think it's that bad, and well, if it works for you, that's all that matters -- right?

                I do have one tip though: AFAIK, you do not need to add the flowController field to your class. Instead, you can simply cast the Object handler parameter in resolveSwfException() to a FlowController. You could/should, of course, check its type first via instanceof.

                So why don't you give that a try?

                If it doesn't work (i.e., if the handler passed in is not the FlowController you expect), please let me know. Actually, I would appreciate it if you let us know in any either case, for success or failure.

                Thanks in advance,

                Sam

                Comment


                • #23
                  Originally posted by sbrannen
                  I do have one tip though: AFAIK, you do not need to add the flowController field to your class. Instead, you can simply cast the Object handler parameter in resolveSwfException() to a FlowController. You could/should, of course, check its type first via instanceof.

                  So why don't you give that a try?
                  Thanks for quick replay. I search, how to get an instance of FlowController and it is!
                  I follow Your tips and here my corected class:
                  Code:
                  /*
                   * Copyright 2002-2006 the original author or authors.
                   *
                   * Licensed under the Apache License, Version 2.0 (the "License");
                   * you may not use this file except in compliance with the License.
                   * You may obtain a copy of the License at
                   *
                   *      http://www.apache.org/licenses/LICENSE-2.0
                   *
                   * Unless required by applicable law or agreed to in writing, software
                   * distributed under the License is distributed on an "AS IS" BASIS,
                   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                   * See the License for the specific language governing permissions and
                   * limitations under the License.
                   */
                  /**
                   * created 2006-08-09
                   */
                  package net.chrisrichardson.foodToGo.webflow;
                  
                  import javax.servlet.http.HttpServletRequest;
                  import javax.servlet.http.HttpServletResponse;
                  
                  import org.apache.commons.lang.ClassUtils;
                  import org.springframework.web.servlet.ModelAndView;
                  import org.springframework.webflow.ExternalContext;
                  import org.springframework.webflow.context.servlet.ServletExternalContext;
                  import org.springframework.webflow.executor.mvc.FlowController;
                  import org.springframework.webflow.executor.support.FlowExecutorArgumentExtractor;
                  import org.springframework.webflow.executor.support.SpringWebFlowExceptionResolver;
                  
                  /**
                   * FoodToGoSWFExceptionResolver is a custom extension of {@link SpringWebFlowExceptionResolver}
                   * @see org.springframework.webflow.executor.support.SpringWebFlowExceptionResolver
                   * @see org.springframework.web.servlet.handler.SimpleMappingExceptionResolver
                   * @author <a href="http://www.witalis.neostrada.pl/foodtogo.html" title="Food To Go sample application">Witold Buszkiewicz</a>
                   *
                   */
                  public class FoodToGoSWFExceptionResolver extends
                  		SpringWebFlowExceptionResolver {
                  	@Override
                  	/**
                  	 * @return A ModelAndView which will launch a new flow, when current flow is active. When flow is ended default flow will launch 
                  	 * (if FlowController defaultFlowId is set) or defaultErrorView will display 
                  	 */
                  	protected ModelAndView resolveSwfException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) {
                          String servletPath = request.getServletPath();
                          ModelAndView modelAndView = null;
                          if (LOG.isDebugEnabled()) {
                              LOG.debug("servletPath [" + servletPath + "].");
                          }
                          if (handler instanceof FlowController) {
                  	        FlowController flowController = (FlowController)handler;
                  	        FlowExecutorArgumentExtractor feae = flowController.getArgumentExtractor();
                  	        String flowIdParameterName = feae.getFlowIdParameterName();
                  	        ExternalContext externalContext = new ServletExternalContext(null, request, response); 
                  	        String flowId = feae.extractFlowId(externalContext);
                  	        servletPath = servletPath + "?" + flowIdParameterName + "=" + flowId;
                  	        if (LOG.isDebugEnabled()) {
                  	            LOG.debug("Processed SWF exception [" + ClassUtils.getShortClassName(exception.getClass())
                  	                    + "]. Redirecting to [" + servletPath + "].");
                  	        }
                  	        modelAndView = getModelAndView("redirect:" + servletPath, exception, request);
                          }
                  		return modelAndView;
                  	}
                  }
                  and context after modification:
                  Code:
                  	<bean name="/flowController.htm" class="org.springframework.webflow.executor.mvc.FlowController">
                  		<property name="flowExecutor" ref="flowExecutor"/>
                  		<property name="cacheSeconds" value="5"/>
                  		<property name="defaultFlowId" value="placeOrder-flow"/>
                  	</bean>
                  	<bean id="globalExceptionResolver" class="net.chrisrichardson.foodToGo.webflow.FoodToGoSWFExceptionResolver">
                  		<property name="supportedSwfExceptions">
                  			<list>
                  				<value>org.springframework.webflow.execution.repository.NoSuchFlowExecutionException</value>
                  			</list>
                  		</property>
                  		<property name="defaultErrorView" value="index" />
                  	</bean>
                  It was tested and still works!

                  regards
                  Witek.

                  Comment


                  • #24
                    Originally posted by witalis3
                    Thanks for quick replay. I search, how to get an instance of FlowController and it is!

                    ...

                    It was tested and still works!
                    Great!

                    Your code looks much cleaner now and easier to use.

                    Glad you worked it out.

                    FYI: I'll try to combine your code and my code to create the first two pluggable strategy implementations for SWF exeception resolving. With these two implementations, I think most standard use cases will be covered.

                    Thanks for sharing your code.

                    Regards,

                    Sam

                    Comment


                    • #25
                      Thanks for providing the code.
                      I copied the code and tried to get it working but I have a problem. May be I am missing something here... If I have multiple flows ... i can't set a defaultFlowId for my FlowExecutor and _flowId parameter is not passed around in the URL once the flow kicks-off.... how do I extract what flow to start from available info ?(which seems to be just the expired _flowExecutionKey)?

                      Comment


                      • #26
                        could not catch NoSuchFlowExecutionException

                        I am trying to use the swf struts birthdate example and put the SpringWebFlowExceptionResolver in my code, but the NoSuchFlowExecutionException still is throwed outside my application.
                        Anything else do I need to do besides adding it to applicationContext.xml
                        Here is my applicationContext.xml
                        Code:
                        <beans>
                        	<bean id="globalExceptionResolver" class="org.springframework.webflow.executor.support.SpringWebFlowExceptionResolver">
                        		<property name="supportedSwfExceptions">
                        		<list>
                        			<value>org.springframework.webflow.execution.NoSuchFlowDefinitionException</value>
                        			<value>org.springframework.webflow.execution.repository.NoSuchFlowExecutionException</value>
                        			<value>org.springframework.webflow.conversation.NoSuchConversationException</value>
                        			<value>org.springframework.webflow.executor.support.FlowExecutorArgumentExtractionException</value>
                        		</list>
                        		</property>
                        		<property name="defaultErrorView" value="birthdateForm" />
                        	</bean>
                        </beans>
                        Code:
                        2007-03-05 13:42:52,093 ERROR [org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/swf_date].[action]] - <Servlet.service() for servlet action threw exception>
                        org.springframework.webflow.execution.repository.NoSuchFlowExecutionException: No flow execution could be found with key '_c654AC1DB-D558-B0DD-7C5D-31900A3BA54F_k0E04E495-E1AD-E335-72D9-E883B473BED4' -- perhaps this executing flow has ended or expired? This could happen if your users are relying on browser history (typically via the back button) that references ended flows.; nested exception is org.springframework.webflow.conversation.NoSuchConversationException: No conversation could be found with id '654AC1DB-D558-B0DD-7C5D-31900A3BA54F' -- perhaps this conversation has ended? 
                        Caused by: 
                        org.springframework.webflow.conversation.NoSuchConversationException: No conversation could be found with id '654AC1DB-D558-B0DD-7C5D-31900A3BA54F' -- perhaps this conversation has ended? 
                        	at org.springframework.webflow.conversation.impl.ConversationContainer.getConversation(ConversationContainer.java:91)
                        	at org.springframework.webflow.conversation.impl.SessionBindingConversationManager.getConversation(SessionBindingConversationManager.java:109)
                        	at org.springframework.webflow.execution.repository.support.AbstractConversationFlowExecutionRepository.getConversation(AbstractConversationFlowExecutionRepository.java:239)
                        	at org.springframework.webflow.execution.repository.support.AbstractConversationFlowExecutionRepository.getLock(AbstractConversationFlowExecutionRepository.java:124)
                        	at org.springframework.webflow.executor.FlowExecutorImpl.refresh(FlowExecutorImpl.java:268)
                        	at org.springframework.webflow.executor.support.FlowRequestHandler.handleFlowRequest(FlowRequestHandler.java:122)
                        	at org.springframework.webflow.executor.struts.FlowAction.execute(FlowAction.java:218)
                        	at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:419)
                        	at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:224)
                        	at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1194)
                        	at org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:414)
                        	at javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
                        	at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
                        	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
                        	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                        	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
                        	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
                        	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
                        	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
                        	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
                        	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
                        	at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:833)
                        	at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:639)
                        	at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1285)
                        	at java.lang.Thread.run(Thread.java:595)

                        Comment


                        • #27
                          Hi Wang,

                          First and foremost, SpringWebFlowExceptionResolver has only been tested in conjunction with Spring MVC as the view/controller technology. Thus I'm not really sure that you can even use the SpringWebFlowExceptionResolver "as is" with Struts, but let's look at your problem in more detail to see if we can help...

                          Originally posted by wang View Post
                          I am trying to use the swf struts birthdate example and put the SpringWebFlowExceptionResolver in my code, but the NoSuchFlowExecutionException still is throwed outside my application.
                          Anything else do I need to do besides adding it to applicationContext.xml
                          With Spring MVC, that's all you'd need; with Struts that unfortunately doesn't appear to be the case.

                          Originally posted by wang View Post
                          Here is my applicationContext.xml
                          (snip)
                          Your config looks fine as well.

                          Originally posted by wang View Post
                          Code:
                          2007-03-05 13:42:52,093 ERROR [org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/swf_date].[action]] - <Servlet.service() for servlet action threw exception>
                          org.springframework.webflow.execution.repository.NoSuchFlowExecutionException: No flow execution could be found with key '_c654AC1DB-D558-B0DD-7C5D-31900A3BA54F_k0E04E495-E1AD-E335-72D9-E883B473BED4' -- perhaps this executing flow has ended or expired? This could happen if your users are relying on browser history (typically via the back button) that references ended flows.; nested exception is org.springframework.webflow.conversation.NoSuchConversationException: No conversation could be found with id '654AC1DB-D558-B0DD-7C5D-31900A3BA54F' -- perhaps this conversation has ended?
                          Based on your stack trace, it appears that SpringWebFlowExceptionResolver is never coming into the picture: it's simply never called in program execution.

                          I am not a Struts expert, but I believe I have an idea of what is going here. Perhaps Keith or somebody with more Struts experience can verify my assumptions.

                          In struts-config.xml for the birthdate example, you have something like the following:

                          Code:
                          	<action-mappings>
                          		<action path="/home" forward="index.jsp"/>
                          		<action path="/flowAction" name="actionForm" scope="request" type="org.springframework.webflow.executor.struts.FlowAction"/>
                          	</action-mappings>
                          And from web.xml, you configured a Struts ActionServlet to act as your front-end controller, similar to the following:

                          Code:
                          	<servlet>
                          		<servlet-name>action</servlet-name>
                          		<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
                          	</servlet>
                          The difference between this configuration for using Struts and the analogous configuration for using Spring MVC is that the program's flow of control passes from the Struts ActionServlet to SWF's FlowAction, which then delegates to the internals of SWF. Your FlowAction is, however, not managed by your Spring ApplicationContext. Thus, to make a long story short, when an SWF exception is thrown in FlowAction's execute() method, the unhandled exception trickles back up to the Struts ActionServlet instead of to Spring's ApplicationContext, and the globalExceptionResolver you configured never gets a chance to handle the exception.

                          Make sense?! :o

                          You'd probably also like a solution to your problem. So I came up with two possibilities:
                          1. Custom Struts ExceptionHandler
                          2. Spring's DelegatingActionProxy

                          You can try them out and let us know if they work.

                          I did some quick research and came across this page:

                          http://www.objectsource.com/j2eechap...n_Handling.htm

                          Skim down to section "18.7 Exception handling Struts way". You'll see there that you can configure custom exception handlers (i.e., that extend org.apache.struts.action.ExceptionHandler) in action mappings in struts-config.xml. Basically, you would have to devise a way to perform the same processing provided by SpringWebFlowExceptionResolver in a custom Struts ExceptionHandler.

                          But... there may be an easy way out! You might be able to use Spring's DelegatingActionProxy (see the JavaDoc for details: org.springframework.web.struts.DelegatingActionPro xy) to have your FlowAction managed by Spring's ApplicationContext. I haven't tested it, but I think/hope that would allow your globalExceptionResolver to be able to handle the exception thrown from your FlowAction.

                          Good Luck!!! (and please let us know how it turns out)

                          Sam

                          Comment


                          • #28
                            handling end states in struts

                            Hi Sam,
                            Thanks for your quick response.
                            I have been trying the Spring's DelegatingActionProxy for a while, it gives me NullPointerException. Here is what I did
                            In applicationContext.xml
                            Code:
                            	<bean name="/flowAction"   
                                    class="org.springframework.webflow.executor.struts.FlowAction">
                              	</bean>
                            and in struts-config.xml
                            Code:
                             <!-- 
                            		<action path="/flowAction" name="actionForm" scope="request" type="org.springframework.webflow.executor.struts.FlowAction"/>
                            		-->
                            		
                            		<action path="/flowAction" name="actionForm" scope="request" type="org.springframework.web.struts.DelegatingActionProxy"/>
                            But I keep getting the exeception
                            Code:
                            **** detActionBeanName /flowAction
                            beanName /flowAction
                            Action class org.apache.struts.action.Action
                            ****** action org.springframework.webflow.executor.struts.FlowAction@998537
                            ****** delegateAction org.springframework.webflow.executor.struts.FlowAction@998537
                            ****** mapping ActionConfig[path=/flowAction,name=actionForm,scope=request,type=org.springframework.web.struts.DelegatingActionProxy
                            ****** form org.springframework.web.struts.SpringBindingActionForm@f8e24a
                            ****** request  org.apache.catalina.connector.RequestFacade@10608e6
                            ****** response org.apache.catalina.connector.ResponseFacade@1325877
                            2007-03-08 18:22:43,823 WARN [org.apache.struts.action.RequestProcessor] - <Unhandled Exception thrown: class java.lang.NullPointerException>
                            2007-03-08 18:22:43,823 ERROR [org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/swf_date].[action]] - <Servlet.service() for servlet action threw exception>
                            java.lang.NullPointerException
                            	at org.springframework.web.struts.ActionSupport.getServletContext(ActionSupport.java:121)
                            	at org.springframework.webflow.executor.struts.FlowAction.execute(FlowAction.java:217)
                            	at org.springframework.web.struts.DelegatingActionProxy.execute(DelegatingActionProxy.java:113)
                            	at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:419)
                            	at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:224)
                            	at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1194)
                            	at org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:414)
                            	at javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
                            	at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
                            	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
                            	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                            	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
                            	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
                            	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
                            	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
                            	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
                            	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
                            	at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:833)
                            	at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:639)
                            	at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1285)
                            	at java.lang.Thread.run(Thread.java:595)
                            I put some println in DelegatingActionProxy.java seems it got everything, from the output before exception track
                            Code:
                            	public ActionForward execute(
                            			ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)
                            			throws Exception {
                            
                            		Action delegateAction = getDelegateAction(mapping);
                            		System.out.println("****** delegateAction "+delegateAction);
                            		System.out.println("****** mapping "+mapping);
                            		System.out.println("****** form "+form);
                            		System.out.println("****** request  "+request);
                            		System.out.println("****** response "+response);
                            		
                            		ActionForward forward = null;
                            		forward = delegateAction.execute(mapping, form, request, response);  //Exception is thrown here
                                            
                            		System.out.println("****** forward "+forward);
                            		return forward;
                            	}
                            Any idea? Thanks

                            Comment


                            • #29
                              Follow my previous post, I found that my Spring webApplicationContext is null, does anyone know how to set it up, so it can work together with Struts?
                              Thanks

                              Comment


                              • #30
                                ContextLoaderPlugin

                                See:

                                http://www.springframework.org/docs/...derPlugIn.html

                                Comment

                                Working...
                                X