Announcement Announcement Module
Collapse
No announcement yet.
How do you override or extend the flowExecutionKey? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • How do you override or extend the flowExecutionKey?

    The goal here is to add information to the flowExecutionKey that tells me the information I need to know in order to load up saved user information so that if a server node drops out I will be able to failover to another server without out any (or limited) impact on the user. (i.e. A user is in the middle of a flow and the flow execution is currently paused so that he/she can enter in data...meanwhile the server they are currently connected to goes down...the user then clicks the next button to resume the full flow. I want to add the user id, and a key that specifies where my persistant data is so that when I am transfered to a new server I can load up all the information I need, create a new flow on that server and set the state of that flow)

    Here is what I have found:
    1. Apparently there is an open source project "Terracotta" that aims on doing this for spring webflow. The problem is it seems like it copies your flow execution on each server and in our case if we did that for every user we would probably have memory issues.

    2. I have seen some threads that mention similar issues but havent been able to find some kind of example FlowExecutorArgumentExtractor. I have also found and read the following reference and have been wandering around the following api

    Are there any examples or tutorials out there that show how to add to or override the flowExecutionKey? If not maybe I will have to create one , either way can anyone point me in the right direction so that I can figure out the best way to do this?

    Thanks in advance!

  • #2
    Originally posted by testing123 View Post
    Here is what I have found:
    1. Apparently there is an open source project "Terracotta" that aims on doing this for spring webflow. The problem is it seems like it copies your flow execution on each server and in our case if we did that for every user we would probably have memory issues.
    Thanks in advance!
    Terracotta absolutely does not copy all flowexecutions into all JVMs. Terracotta will solve your problem in 2 ways:

    1. It only pushes flowexecutions to the app instances the end user has been load balanced to (if you have 2 servers or 200, and my web requests only go to box #27, my flowexecutions will only be on box #27 + in Terracotta's server)

    2. It supports partial object graph paging so that you could have a 100MB flow and only 100KB of it is resident (based on LRU / LFU algorithms).

    Full disclosure: I am a Terracotta Employee, but I do believe that Terracotta is purpose-built for what you are trying to achieve.

    Check out the demos in this intro. They show how partial object paging works.

    Comment


    • #3
      after my first post I ...

      Read the following article:
      http://www.infoq.com/articles/spring...low-terracotta

      That made me change my mind about how I was thinking of it. We have "sticky" sessions and it appeared that was a good solution for us. I have since talked to my team lead and he doesn't think its an option...due to various reasons (mostly political) we will not be able to use it. We have already implemented a lot of the code for sending information to the server that would allow whatever server it fails over to to load up the correct user information.

      My original thought was to add a hidden input field with this information but if it is possible I would like to add it to the flowExecutionKey like this:

      _flowExecutionKey=_cD934FA66-05F4-251E-ECCE-EB300DFAB701_kFD8F4B84-81BC-3023-6CF0-92D3EE78E77C_uMY-USER-INFORMATION-HERE

      What I have come up with so far:
      I have extended RequestParameterFlowExecutorArgumentHandler and called my class MyNewFlowExecutorArgumentHandler

      Code:
      	<bean name="/ownerinformation.htm" class="org.springframework.webflow.executor.mvc.FlowController">
      		<property name="flowExecutor" ref="flowExecutor"/>	
      		<property name="argumentHandler">
      			<bean class="com.xactsites.producty.web.MyNewFlowExecutorArgumentHandler" />
      		</property>	
      	</bean>
      I am currently thinking of overriding the following two methods in the class like so:


      Code:
      @Override
      public String createFlowExecutionUrl(String flowExecutionKey, FlowExecutionContext flowExecution,
      		ExternalContext context) {
      	StringBuffer url = new StringBuffer();
      	appendFlowExecutorPath(url, context);
      	url.append('?');
      	appendQueryParameter(url, getFlowExecutionKeyArgumentName(), flowExecutionKey + _uMY-USER-INFORMATION-HERE);
      	return url.toString();
      }
      
      @Override
      public String extractFlowExecutionKey(ExternalContext context) throws FlowExecutorArgumentExtractionException {
      	String encodedKey = context.getRequestParameterMap().get(getFlowExecutionKeyArgumentName());
      	// Parse off the user information here...
      	if (!StringUtils.hasText(encodedKey)) {
      		throw new FlowExecutorArgumentExtractionException(
      				"Unable to extract the flow execution key parameter: make sure the client provides the '"
      				+ getFlowExecutionKeyArgumentName()
      				+ "' parameter as input; the parameters provided in this request are: "
      				+ StylerUtils.style(context.getRequestParameterMap()));
      	}
      	return encodedKey;
      }
      Pretty ugly eh? There has got to be a more elegant solution...right? Anybody have any ideas?

      Comment


      • #4
        I don't think rolling your own solution by adding stuff onto the flowExecutionKey is the best option.

        Terracotta should be able to handle this.

        Another option you have is implementing your own ConversationManager. By default SWF uses a SessionBindingConversationManager, which stores conversational data in the HttpSession. You could write your own ConversationManager, for instance storing data in a database available to all your web servers. That will get you conversation failover from one server to another without messing with the flowExecutionKey.

        If that's not an option either, using a custom FlowExecutorArgumentHandler is probably the best way to go.

        Erwin

        Comment


        • #5
          Originally posted by klr8 View Post
          I don't think rolling your own solution by adding stuff onto the flowExecutionKey is the best option.

          Terracotta should be able to handle this.

          Another option you have is implementing your own ConversationManager. By default SWF uses a SessionBindingConversationManager, which stores conversational data in the HttpSession. You could write your own ConversationManager, for instance storing data in a database available to all your web servers. That will get you conversation failover from one server to another without messing with the flowExecutionKey.

          If that's not an option either, using a custom FlowExecutorArgumentHandler is probably the best way to go.

          Erwin
          Thanks for your response Erwin. Right now the custom FlowExecutorArgumentHandler seems like the only option for us...even if its not the best one. My hands are kind of tied on this one

          There are two methods that I think I need to override. Does everything go through these two methods? Are there additional methods I should override?

          Code:
          public abstract String extractFlowExecutionKey(ExternalContext context)
          			throws FlowExecutorArgumentExtractionException;
          
          public void exposeFlowExecutionContext(String flowExecutionKey, FlowExecutionContext context, Map model) {
          		if (flowExecutionKey != null) {
          			model.put(getFlowExecutionKeyAttributeName(), flowExecutionKey);
          		}
          		model.put(getFlowExecutionContextAttributeName(), context);
          	}
          Thanks again for all of your help everyone!

          Comment


          • #6
            Does everything go through these two methods?
            Yes.

            Erwin

            Comment


            • #7
              What about creating a new FlowExecutionRepository and FlowExecutionKey

              After a little more digging it looks like I may want to create my own FlowExecutionRepository and FlowExecutionKey. That way I can control the parsing of the flowExecutionKey. In my new flowExecutionKey I will add my own userInformation id that I will be able to parse off and add in the FlowExecutionRepository. Any comments on this (good or bad)?

              My new question is where to use this userInformation id. Ideally it would be in the same place that a conversation is loaded up. That way I can first see if the conversation is in the session. If it is, great, keep doing what your doing. If it is not then use the userInformation id that I have to repopulate the needed information (ie. create a new flow, put it in the right state, repopulate data from the form submissions, and relogin the user with their user specific preferences) and then keep doing what your doing.

              Perhaps to do this I will also have to create my own ConversationManager as mentioned above. Is this a correct assumption?

              Comment


              • #8
                I just found some good info

                http://forum.springframework.org/arc...p/t-34008.html


                which also refers to:
                http://www.ervacon.com/products/swf/tips/tip2.html

                (Erwin...you are a busy man )

                Comment


                • #9
                  General concepts

                  What belongs in the X-servlet-config.xml versus the X-webflow-config.xml. The link I mention above show the following config stuff:

                  Code:
                  <bean id="flowExecutor" class="org.springframework.webflow.executor.FlowExecutorImpl">
                  		<constructor-arg ref="flowRegistry"/>
                  		<constructor-arg>
                  			<bean class="org.springframework.webflow.engine.impl.FlowExecutionImplFactory">
                  				<property name="executionAttributesMap" ref="executionAttributes"/>
                  			</bean>
                  		</constructor-arg>
                  		<constructor-arg ref="repository"/>
                  	</bean>
                  	
                  	<bean id="repository"
                  		class="org.springframework.webflow.execution.repository.continuation.ContinuationFlowExecutionRepository">
                  		<constructor-arg>
                  			<bean class="org.springframework.webflow.engine.impl.FlowExecutionImplStateRestorer">
                  				<constructor-arg ref="flowRegistry"/>
                  				<property name="executionAttributesMap" ref="executionAttributes"/>
                  			</bean>
                  		</constructor-arg>
                  		<constructor-arg ref="conversationManager"/>
                  		<property name="maxContinuations" value="30"/>
                  	</bean>
                  	
                  	<bean id="conversationManager"
                  		class="org.springframework.webflow.conversation.impl.SessionBindingConversationManager">
                  		<property name="maxConversations" value="1"/>
                  	</bean>
                  	
                  	<bean id="executionAttributes" class="org.springframework.beans.factory.config.MapFactoryBean">
                  		<property name="sourceMap">
                  			<map>
                  				<entry key="alwaysRedirectOnPause">
                  					<value type="java.lang.Boolean">true</value>
                  				</entry>
                  			</map>
                  		</property>
                  	</bean>
                  That belongs in the X-webflow-config.xml....right? The reason I am using this instead of the simpler new stuff with swf 1.0.1 is so that I can have control over what custom classes I want to plugin.

                  Comment


                  • #10
                    Its working...so far...

                    I created my own FlowExecutionKey (extends FlowExecutionKey) and FlowExecutionRepository (extends SimpleFlowExecutionRepository) and adjusted the code to handle the 3rd id (in my case this contains info needed to "rehydrate" a session with the user information). The 3rd id is now a part of the flowExecutionKey and it appears to be working (I put in a dummy 3rd id and it is printed out correctly).

                    My next goal was to make a custom ConversationManager so that I could handle the getting of conversations. So I made a ConversationManager that extends SessionBindingConversation. In this manager I override the "getConversation(ConversationId id)". I was hoping to reuse a lot of the helper classes (i.e. ConversationContainer, ContainedConversation) but was unable to because both of these classes are not public classes. I assume this is by design.

                    Now comes my question. Why not make these classes public? If I can extend the SessionBindingConversationManager why not make its helper classes visible? I realize that I am probably one of the few if the only one to try and do this, I just don't see why I would have to create my own helper functions that would work just the same as the ones in SessionBindingConversationManager.

                    Hopefully I am not chopping up and mixing code in the wrong manner. I have just been so impressed by the flexibility of swf that when I ran into this it seemed little odd. Let me know what you think.
                    Thanks

                    Comment


                    • #11
                      Nice to hear things are starting to work out!

                      Now comes my question. Why not make these classes public? If I can extend the SessionBindingConversationManager why not make its helper classes visible?
                      As you already guessed, this is by design. These helper classes are internal implementation artifacts of the SessionBindingConversationManager. By making them package private, we keep them out of the user visible API. This gives us more freedom refactoring these classes since we don't have to worry about breaking user applications.

                      Erwin

                      Comment


                      • #12
                        true...

                        Originally posted by klr8 View Post
                        This gives us more freedom refactoring these classes since we don't have to worry about breaking user applications.
                        That makes sense. Thanks! I'll post everything when I get it up and working.

                        Comment

                        Working...
                        X