Announcement Announcement Module
Collapse
No announcement yet.
Spring Webflow 2 Working With Persistent Conversation Data Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring Webflow 2 Working With Persistent Conversation Data

    Hi all,

    As default behavior, SWF stores conversation data in session and there could exist situations where it is needed to persist that conversation data in a non volatile data warehouse. For instance, in my case was required to "pause" a flow on determined states allowing the user to restore the flow exactly in the same point the user stopped his work. Initially, achieving that was a big challenge for me because there is not too much information about how to do that and after several weeks googling, reading and developing I have my flows working with persisted conversation data.

    Well, so I have decided to post the solution for those who have similar requirements when using SWF. My solution works with conversation data persisted on DB (MySQL) and is based on the solution approached in the book The Definitive Guide to Spring Web Flow (http://www.apress.com/9781430216247/) by Erwin Vervaet, creator and one of the fathers of Spring Webflow. The original solution is useful if you are working with SWF 1 so I have adapted that for working on SWF2. At the same time, I am posting it with the intention of receiving comments and possible improvements for both the code and the implementation because I am sure that it is possible to make it much better!

    Basically it is required to implement three classes:
    • PersistentConversation: replacement for the original ContainedConversation SWF2's implementation.
    • PersistentConversationManager: replacement for the original SessionBindingConversationManager SWF2's implementation.
    • PersistentConversationHolder: replacement for the original ConversationContainer SWF2's implementation

    Aditionally, I also needed a PersistentConversationHelper for unify business logic invocation and a JdkConcurrentConversationLock copied from SWF1 source code (I know it is very tricky but I need to solve the problem ASAP, any alternative?). At the same time, I have a business logic and DAO classes for CRUD operations which I will not post because I think its too obvious (but if somebody requires it, feel free to ask for it).

    And now, here you go the code (original code of Erwin Vervaet still remains but commented):
    • PersistentConversation
      Code:
      public class PersistentConversation implements Conversation, Serializable {
      	
      	/**
      	 * 
      	 */
      	private static final long serialVersionUID = -3410255383231814217L;
      	
      	private ConversationId id;
      	private Map<Object, Object> attributes;
      	
      //	private transient ConversationDao dao;
      	
      	private ConversationLock lock = new JdkConcurrentConversationLock();
      	private int lockCount = 0;
      	private boolean ended = false;
      	
      //	protected PersistentConversation() {
      //	}
      	
      //	public PersistentConversation(ConversationId id, ConversationDao dao) {
      //		this.id = id;
      //		this.attributes = new HashMap<Object, Object>();
      //		this.dao = dao;
      //	}
      	/**
      	 * Constructor to be used for instancing a new PersistentConversation to be persisted.
      	 * 
      	 * @param id
      	 */
      	public PersistentConversation(ConversationId id, String flowName {
      		this.id = id;
      		this.attributes = new HashMap<Object, Object>();
      		this.attributes.put("name", "flows/green-mode-method");
      	}
      	/**
      	 * Constructor to be used for instancing a PersistentConversation already persisted.
      	 * 
      	 * @param webFlowConversation
      	 */
      	public PersistentConversation(WebFlowConversation webFlowConversation) {
      		setIdAsString(webFlowConversation.getId());
      		setAttributesAsByteArray(webFlowConversation.getAttributes());
      	}
      
      	public ConversationId getId() {
      		return id;
      	}
      	
      	protected void setId(ConversationId id) {
      		this.id = id;
      	}
      
      	public void lock() {
      		lock.lock();
      		lockCount++;
      	}
      
      	public Object getAttribute(Object name) {
      		return attributes.get(name);
      	}
      
      	public void putAttribute(Object name, Object value) {
      		attributes.put(name, value);
      	}
      
      	public void removeAttribute(Object name) {
      		attributes.remove(name);
      	}
      
      	public void end() {
      //		dao.deleteConversation(id);
      		PersistentConversationHelper.deleteConversation(id);
      		ended = true;
      	}
      
      	public void unlock() {
      		lockCount--;
      		lock.unlock();
      		if (lockCount == 0) {
      			if (!ended) {
      //				dao.updateConversation(this);
      				PersistentConversationHelper.updateConversation(this);
      			}
      			PersistentConversationHolder.removeConversation(id);
      		}
      	}
      	
      	@Override
      	public String toString() {
      		return new ToStringCreator(this).append("id", id).append("attributes", attributes).toString();
      	}
      	
      	// persistence helpers
      	
      //	public void setDao(ConversationDao dao) {
      //		this.dao = dao;
      //	}
      
      	protected String getIdAsString() {
      		return getId().toString();
      	}
      	
      	protected void setIdAsString(String id) {
      		setId(new SimpleConversationId(id));
      	}
      
      	protected byte[] getAttributesAsByteArray() {
      		try {
      			ByteArrayOutputStream bout = new ByteArrayOutputStream();
      			ObjectOutputStream oout = new ObjectOutputStream(bout);
      			oout.writeObject(attributes);
      			oout.flush();
      			return bout.toByteArray();
      		}
      		catch (Exception e) {
      			// should not happen
      			throw new RuntimeException("Exception serializing attributes map", e);
      		}
      	}
      
      	@SuppressWarnings("unchecked")
      	protected void setAttributesAsByteArray(byte[] bytes) {
      		try {
      			this.attributes = (Map<Object, Object>)
      					new ObjectInputStream(new ByteArrayInputStream(bytes)).readObject();
      		}
      		catch (Exception e) {
      			// should not happen
      			throw new RuntimeException("Exception deserializing attributes map", e);
      		}
      	}
      }
    • PersistentConversationManager
      Code:
      @Component(value = "persistentConversationManager")
      public class PersistentConversationManager implements ConversationManager {
      	
      //	private UidGenerator conversationIdGenerator = new RandomGuidUidGenerator();
      	
      //	private ConversationDao conversationDao;
      	
      //	public void setConversationDao(ConversationDao conversationDao) {
      //		this.conversationDao = conversationDao;
      //	}
      	
      	public Conversation beginConversation(ConversationParameters conversationParameters)
      			throws ConversationException {
      //		ConversationId convId = new SimpleConversationId(conversationIdGenerator.generateUid());
      		ConversationId convId = new SimpleConversationId(UUID.randomUUID());
      //		conversationDao.createConversation(new PersistentConversation(convId, conversationDao));
      		PersistentConversationHelper.createConversation(new PersistentConversation(convId, conversationParameters.getName()));
      		
      		return getConversation(convId);
      	}
      	
      	public Conversation getConversation(ConversationId id) throws ConversationException {
      		if (PersistentConversationHolder.holdsConversation(id)) {
      			// we already loaded the conversation for the calling thread
      			return PersistentConversationHolder.getConversation(id);
      		}
      		else {
      			// load the conversation
      //			PersistentConversation conversation = conversationDao.readConversation(id);
      //			if (conversation == null) {
      //				throw new NoSuchConversationException(id);
      //			}
      			WebFlowConversation webFlowConversation = PersistentConversationHelper.readConversation(id);
      			if (webFlowConversation == null) {
      				throw new NoSuchConversationException(id);
      			}
      			
      			PersistentConversation conversation = new PersistentConversation(webFlowConversation);
      			
      			// cache it for the calling thread
      			PersistentConversationHolder.putConversation(conversation);
      			return conversation;
      		}
      	}
      	
      	public ConversationId parseConversationId(String encodedId) throws ConversationException {
      //		return new SimpleConversationId(conversationIdGenerator.parseUid(encodedId));
      		return new SimpleConversationId(encodedId);
      	}
      }
    • PersistentConversationHolder
      Code:
      public class PersistentConversationHolder {
      
      	public static final ThreadLocal<Map<ConversationId, PersistentConversation>> conversations =
      		new ThreadLocal<Map<ConversationId, PersistentConversation>>() {
      			protected Map<ConversationId, PersistentConversation> initialValue() {
      				return new HashMap<ConversationId, PersistentConversation>();
      			}
      		};
      
      	public static boolean holdsConversation(ConversationId id) {
      		return conversations.get().containsKey(id);
      	}
      	
      	public static void assertHoldsConversation(ConversationId id) throws NoSuchConversationException {
      		if (!holdsConversation(id)) {
      			throw new NoSuchConversationException(id);
      		}
      	}
      	
      	public static PersistentConversation getConversation(ConversationId id) throws NoSuchConversationException {
      		assertHoldsConversation(id);
      		return conversations.get().get(id);
      	}
      	
      	public static void putConversation(PersistentConversation conversation) throws NoSuchConversationException {
      		conversations.get().put(conversation.getId(), conversation);
      	}
      	
      	public static void removeConversation(ConversationId id) throws NoSuchConversationException {
      		assertHoldsConversation(id);
      		conversations.get().remove(id);
      	}
      }

    Almost I forget... This the script of the table where I am storing the conversation data:
    Code:
    CREATE TABLE `web_flow_conversations` (
      `ID` varchar(50) NOT NULL,
      `ATTRIBUTES` blob NOT NULL,
      PRIMARY KEY (`ID`)
    )
    So that's all at the moment. I hope it's useful for others and I expect to receive suggestions and comments for improve the code.

    Thanks.
    Last edited by alejandrogarciaseco; Sep 25th, 2012, 08:03 AM. Reason: Forgot something...

  • #2
    Hi Alejandro
    I was so relieved to find this post as i have been googling for this functionality for past few days and not much help.

    I am also working on SWF2 and have same requirement but i am not that much acquainted with SWF's flow persistence and still learning.

    Please post the more code (if possible) if you were able to make it working.Or if you can provide some more details .

    Thanks

    Comment


    • #3
      Originally posted by Misha79 View Post
      Hi Alejandro
      I was so relieved to find this post as i have been googling for this functionality for past few days and not much help.

      I am also working on SWF2 and have same requirement but i am not that much acquainted with SWF's flow persistence and still learning.

      Please post the more code (if possible) if you were able to make it working.Or if you can provide some more details .

      Thanks
      Hi Misha79,

      I'm proud about you found my post useful.

      I was able to make it work and it's currently working in my application correctly in a development environment. At the moment it seems stable and performs pretty good, despite of I am not still happy at all with the implementation, in my opinion it should be revised in terms of efficiency and elegance.

      Well, what do you need exactly? What's missing in your opinion? Make me know and I will complete the post.

      By the way, I strongly recommend you downloading and studying the source code of SWF1, SWF2 as well as the original Erwin's example of persistent conversation data on SWF1. The Erwin's book is also an interesting tool.

      Regards.

      Comment


      • #4
        Thanks Alejandro,
        For replying as i was desparately checking reply to this post everyday.Finally i starting working on something else and today i saw the reply and i was very happy.


        Well my requirement is:--

        I have flow in which user can save and exit anytime from any page in flow.
        Once he logins inback to the application,i want user to be directed to same pagenumber where he 'saved & exited' from.
        Also all the user-entries to pages should be saved in db and will be retained when he logs in.
        Eg He exits from PPage num 5 and logs out.He logs in and he directly sees page num 5 and he can still navigate back to pages 4,3,1 by clicking 'back' buttons and he should see data he entered before.

        I have gone through 'Erwins Definitive guide' and which is very informative.I started developing my code as per your post & code snippets but couldnot make it work .

        First of all i am not even able to configure the webflow.xml .

        I have custom java files for
        -FlowExecutionRepository
        -Conversation
        -conversationDao
        -conversationHelper
        -conversationManager
        -JdkConcurrentConversationLock (as your example)

        My xml - bold line has some issues.

        Code:
           <!-- Executes flows: the entry point into the Spring Web Flow system -->
           
             <webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry"    >
                <webflow:flow-execution-listeners>
                    <webflow:listener ref="customFloeExecutionListener" criteria="main-flow"/>
                </webflow:flow-execution-listeners>
              <webflow:flow-execution-repository   conversation-manager="customConversationManager" max-executions="5" >
             </webflow:flow-execution-repository>
                
            </webflow:flow-executor>
        
        
        
        <bean id="customFlowExecutionRepository" class="com.persistent.flows..CustomFlowExecutionRepository">
        	<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="customConversationManager"/>
        	
        </bean>

        I am going to read everything again over the weekend and restart.

        Thanks again .





        Originally posted by alejandrogarciaseco View Post
        Hi Misha79,

        I'm proud about you found my post useful.

        I was able to make it work and it's currently working in my application correctly in a development environment. At the moment it seems stable and performs pretty good, despite of I am not still happy at all with the implementation, in my opinion it should be revised in terms of efficiency and elegance.

        Well, what do you need exactly? What's missing in your opinion? Make me know and I will complete the post.

        By the way, I strongly recommend you downloading and studying the source code of SWF1, SWF2 as well as the original Erwin's example of persistent conversation data on SWF1. The Erwin's book is also an interesting tool.

        Regards.

        Comment


        • #5
          About webflow configuration, I have few questions.
          I have customFlowExecutionRepository which extends DefaultFlowExecutionRepository and it is annotated too. Also in xml i configured it as bean ( which was not required).
          Similary, I have customConversationManager configured in xml but dont know how to configure customFlowExecution Repository.
          In code - type= "customFlowExecutionRepository" wont work .
          Code:
          <webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry"    >
               <webflow:flow-execution-repository conversation-manager="customConversationManager" 
               max-executions="5" max-execution-snapshots="0" 
               type= "customFlowExecutionRepository"
               
               />
                  <webflow:flow-execution-listeners>
                      <webflow:listener ref="customFlowExecutionListener" criteria="main-flow"/>
                  </webflow:flow-execution-listeners>
                  
                  <webflow:flow-execution-attributes>
                  	<webflow:always-redirect-on-pause value="false" />
              	</webflow:flow-execution-attributes> 
              	    
              </webflow:flow-executor>

          Comment


          • #6
            Well Misha79, it seems you have exactly the same requirements than me so, if I was able to make it work properly you can too!

            Regarding to your issue about configuration, I can tell you that I'm configuring it in a slightly different way. This is the XML snippet for the flow executor with the custom conversation manager:

            Code:
            	<!-- Creates a flow executor in Spring, responsible for creating and executing flows -->
            	<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
            		<!-- Specifies a custom conversation manager and limits the snapshots creation for flows instances -->
            		<webflow:flow-execution-repository max-execution-snapshots="5" conversation-manager="persistentConversationManager" />
            	</webflow:flow-executor>
            And my custom conversation manager is annotated like this:

            Code:
            @Component(value = "persistentConversationManager")
            public class PersistentConversationManager implements ConversationManager {...}
            If you are using annotations you don't need to explicitly declare in the XML your custom conversation manager for wiring.

            By the way, which version of SWF are you working with? And what kind of problem are you facing? Are you having an exception or just it isn't working and nothing else? Try to give me a more detailed explanation of your problem please.

            Good weekend.

            Comment


            • #7
              Hi Alejandro
              I tried exactly same as your xml but when i tried debugging, i dint see control coming to my custom conversation manager class which means it dint bother about my conversation manager. From furthur research , i realized i need to add custom flow execution repository
              which cant be added just like that and i followed post here created like that-
              Code:
              public class  CustomFlowExecutorFactoryBean implements FactoryBean{
              	
              	 private FlowExecutorImpl defaultFlowExecutor;
              	 private Object singleton;
              
              	@Override
              	public Object getObject() throws Exception {
              		 if (defaultFlowExecutor == null) {
              	            throw new FactoryBeanNotInitializedException("defaultFlowExecutor is null ");
              	        }
              	        
              	        if (singleton != null) { return singleton; }
              	        FlowExecutorImpl lFEx = defaultFlowExecutor;
              	        FlowDefinitionLocator lOriginalLocator = lFEx.getDefinitionLocator();
              	        FlowExecutionFactory lOriginalFactory = lFEx.getExecutionFactory();
              	        FlowExecutionRepository lCtqFER = 
              	            new CustomFlowExecutionRepository((DefaultFlowExecutionRepository)lFEx.getExecutionRepository());
              	        singleton = new FlowExecutorImpl(lOriginalLocator, lOriginalFactory, lCtqFER);
              	        return singleton;
              	}
              
              	@Override
              	public Class getObjectType() {
              		return FlowExecutorImpl.class;
              	}
              
              	@Override
              	public boolean isSingleton() {
              		
              		return true;
              	}
              	 /**
                   * @param pDefaultFlowExecutor the defaultFlowExecutor to set
                   */
              	
                  public void setDefaultFlowExecutor(FlowExecutorImpl pDefaultFlowExecutor) {
                      defaultFlowExecutor = pDefaultFlowExecutor;
                  }
              
              }
              
              
              and tweaked xml -
              <bean id="flowController" class="org.springframework.webflow.mvc.servlet.FlowController">
              	    <property name="flowExecutor"  >
              	    <bean id="customFlowExecutor" class="com.myflows.customFlowExecutorFactoryBean">
                  		 <property name="defaultFlowExecutor" ref="flowExecutor1">
                  		</property>
                  	</bean>
              	    </property>
              	    <property name="flowUrlHandler">
              			<bean id="webFlowUrlHandler" class="org.springframework.webflow.context.servlet.WebFlow1FlowUrlHandler"/>
              	    </property>
              	</bean>
              	
              	
              	   <!-- Executes flows: the entry point into the Spring Web Flow system -->
                 
                   <webflow:flow-executor id="flowExecutor1" flow-registry="flowRegistry"  >
                   	<webflow:flow-execution-repository conversation-manager="customPersistentConversationManager"      
                   			max-executions="5" max-execution-snapshots="0"              />
                      	<webflow:flow-execution-listeners>
                          	<webflow:listener ref="customFlowExecutionListener" criteria="root-flow"/>
                      	</webflow:flow-execution-listeners>
                      
                      	<webflow:flow-execution-attributes>
                      		<webflow:always-redirect-on-pause value="false" />
                  		</webflow:flow-execution-attributes> 
                  </webflow:flow-executor>
              And ran into NullPointerException in beginning only.
              also about version, I am actually using SWF2.0 but for this functionality, i added SWF2.3 jars and modified schema reference in xml to SWF 2.3
              With SWF 2.0- "conversation-manager" attribute gives errors in xml but it works with SWF 2.3
              Code:
              <webflow:flow-execution-repository conversation-manager="customPersistentConversationManager"      
                   			max-executions="5" max-execution-snapshots="0"              />
              Otherwise my xml tags were showing errors and i was'nt not at all able to figure out.
              Last edited by Misha79; Sep 9th, 2012, 04:27 PM.

              Comment


              • #8
                Hi,
                I just figured that my custom conversation manager is not at all being detected.(this is without custom flow execution
                repository code or setup) .
                I can type anything to the value(real id or xyz) of attribute conversation-manager ,but i donot get any error.
                This is SWF 2.3


                Code:
                 <webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry"  >
                     	<webflow:flow-execution-repository conversation-manager="type-anything-here-not-detected"      
                     			max-executions="5"              />
                        	<webflow:flow-execution-listeners>
                            	<webflow:listener ref="flowExecutionListener" criteria="main-flow"/>
                        	</webflow:flow-execution-listeners>
                        
                        	<webflow:flow-execution-attributes>
                        		<webflow:always-redirect-on-pause value="false" />
                    		</webflow:flow-execution-attributes> 
                    </webflow:flow-executor>
                Please suggest.

                Comment


                • #9
                  Hi Misha79,

                  Which namespaces are you using in in your XML? With SWF 2.3 I'm using the next ones:

                  Code:
                  <beans xmlns="http://www.springframework.org/schema/beans"
                  	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  	xmlns:mvc="http://www.springframework.org/schema/mvc"
                  	xmlns:context="http://www.springframework.org/schema/context"
                  	xmlns:webflow="http://www.springframework.org/schema/webflow-config"
                  	xsi:schemaLocation="
                          http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
                          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
                          http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.3.xsd">
                  Anyway, I have to make you know that sometimes I also have XML errors related to the webflow: namespace, but I really don't know why. Nevertheless, when I deploy the project to my Tomcat 6.0 it works perfectly.

                  By the way, if you want to use annotations you should add those tags at the beginning of your XML file:

                  Code:
                  	<!-- Scans within the base package of the application for @Components to configure as beans -->
                  	<!-- @Controller, @Service, @Configuration, etc. -->
                  	<context:component-scan base-package="ie.i2e2.greenmode.web" />
                  	
                  	<!-- Enables the Spring MVC @Controller programming model -->
                  	<mvc:annotation-driven conversion-service="applicationConversionService" />
                  Otherwise, you would declare your custom conversation manager explicitly as you originally posted.

                  At the same time, I see that you are configuring your flow-execution-repository in a different way than me; you are specifying flow execution listener and attributes. I don't know how it could affect to the performance, but take it into account. Initially, if I were you I would try to configure SWF in a way as closer as possible and then once it is working, I would add my custom preference step by step checking always that it keeps working. If you want, I can post my whole dispatcher XML.

                  By the way, are you using Maven? I don't believe your problem is libraries issue, but I could post my dependencies to make you able to set up an environment as similar to mine.

                  Regards.

                  Comment


                  • #10
                    conversation manager is being identified by spring now.

                    My app is picking up custom conversation manager now. I had both versions for SWF in my libraries.
                    Now i get this exception- means i have to implement .for create/get conversation
                    ----------------------
                    Code:
                    org.springframework.webflow.conversation.NoSuchConversationException: No conversation could be found with id '19005da1-42e0-4bb5-8b1b-cb8b1eb57721' -- perhaps this conversation has ended?
                    ------------------------
                    Thank you so much. I was struggling for weeks to bring it up.

                    For now, I wrote my DAO to read/write onto a file & will be replaced with real DB call soon.
                    Once it is better position, i will make DB changes. (Its not easy step to modify DB in my case)

                    PersistentConversationHelper - As per your code, should have read/write/update/delete conversation functions.
                    I am trying to fillup code for saving/read conversations now.
                    Can you please help move little furthur with this?


                    Code:
                    @Component(value = "csPersistentConversationHelper")
                    public class CustomPersistentConversationHelper  {
                    	
                    	public static void createConversation(
                    			CustomPersistentConversation csPersistentConversation) {
                    		System.out.println("creating conversation");
                    		//??what next...I  have conversationId in conversationmanager
                    		
                    		
                    	}
                    
                    	public static WebFlowConversation readConversation(ConversationId id) {
                    		// TODO Auto-generated method stub
                    		return null;
                    	}
                    
                    	public static void deleteConversation(ConversationId id) {
                    		// TODO Auto-generated method stub
                    		
                    	}
                    
                    	public static void updateConversation(
                    			CustomPersistentConversation csPersistentConversation) {
                    		// TODO Auto-generated method stub
                    		
                    	}
                    
                    	
                    }
                    On your question, We are not using maven.

                    Comment


                    • #11
                      Misha79, Iīm happy that itīs working for you, at least partially! It seems you are closer, just a little more work on it and it will be working 100%.

                      Well first of all take care about mixing different versions of a same librarie, is not a good practice and perhaps it could have and unexpected behavior in terms of I believe you can't be fully sure of the classloader will load always load the classes of the same librarie, unless you configure it explicitly. I'm not totally sure about that, but it is only an advice.

                      About the error you are talking about, as far as I remember I believe I got the same error when I was implementing the solution. It happens because SWF canīt find a conversation that is suposed to exist. Probably in your scenario it is happening because the code you posted is still incomplete; your conversation manager is trying to load a conversation previously created but the method readConversation(ConversationId id) is always returning null.

                      You must complete the code of your CustomPersistentConversationHelper, otherwise it will never work!! I will post my code for the same class tomorrow when I come back to work, please remember me with a private message if I don't do it, I can easily forget it hehehe

                      Regards and good weekend.

                      Comment


                      • #12
                        Well this is the code of the PersistentConversationHelper:

                        Code:
                        @Component
                        public class PersistentConversationHelper {
                        
                        	private static WebFlowConversationsBo webFlowConversationsBo;
                        	
                        	@Autowired
                        	public PersistentConversationHelper(WebFlowConversationsBo webFlowConversationsBo) {
                        		PersistentConversationHelper.webFlowConversationsBo = webFlowConversationsBo;
                        	}
                        	
                        	public static void createConversation(PersistentConversation conv) {
                        		webFlowConversationsBo.save(new WebFlowConversation(conv.getIdAsString(), conv.getAttributesAsByteArray()));
                        	}
                        	
                        	public static WebFlowConversation readConversation(ConversationId id) {
                        		return webFlowConversationsBo.findOne(id.toString());
                        	}
                        	
                        	public static void updateConversation(PersistentConversation conv) {
                        		webFlowConversationsBo.save(new WebFlowConversation(conv.getIdAsString(), conv.getAttributesAsByteArray()));
                        	}
                        	
                        	public static void deleteConversation(ConversationId id) {
                        		webFlowConversationsBo.delete(id.toString());
                        	}
                        }
                        As you can see, it doesn't have anything special, just a business object invocation. Then, the BO invokes a DAO which has the same methods and arguments which perform CRUD operations. Note that I'm using JPA 2 with Hibernate 3 and Spring Data. It is really up to you which technology use for DB interaction, just have in mind that basically in every scenario is nothing else but methods that execute CREATE, UPDATE, DELETE and SELECT queries.

                        I hope it helps.

                        Regards.

                        Comment


                        • #13
                          Thanks a ton .. Alejandro .. i am going to try this code and will be back with output or questions.


                          .
                          Originally posted by alejandrogarciaseco View Post
                          Well this is the code of the PersistentConversationHelper:

                          Code:
                          @Component
                          public class PersistentConversationHelper {
                          
                          	private static WebFlowConversationsBo webFlowConversationsBo;
                          	
                          	@Autowired
                          	public PersistentConversationHelper(WebFlowConversationsBo webFlowConversationsBo) {
                          		PersistentConversationHelper.webFlowConversationsBo = webFlowConversationsBo;
                          	}
                          	
                          	public static void createConversation(PersistentConversation conv) {
                          		webFlowConversationsBo.save(new WebFlowConversation(conv.getIdAsString(), conv.getAttributesAsByteArray()));
                          	}
                          	
                          	public static WebFlowConversation readConversation(ConversationId id) {
                          		return webFlowConversationsBo.findOne(id.toString());
                          	}
                          	
                          	public static void updateConversation(PersistentConversation conv) {
                          		webFlowConversationsBo.save(new WebFlowConversation(conv.getIdAsString(), conv.getAttributesAsByteArray()));
                          	}
                          	
                          	public static void deleteConversation(ConversationId id) {
                          		webFlowConversationsBo.delete(id.toString());
                          	}
                          }
                          As you can see, it doesn't have anything special, just a business object invocation. Then, the BO invokes a DAO which has the same methods and arguments which perform CRUD operations. Note that I'm using JPA 2 with Hibernate 3 and Spring Data. It is really up to you which technology use for DB interaction, just have in mind that basically in every scenario is nothing else but methods that execute CREATE, UPDATE, DELETE and SELECT queries.

                          I hope it helps.

                          Regards.

                          Comment


                          • #14
                            One question -
                            what is the difference between WebFlowConversation and PersistentConversation classes. Both are custom classes and PersistentConversation implements Conversation interface. What is the need of WebFlowConversation. I just added 2 attributes-
                            Code:
                            public class WebFlowConversation {
                            
                            	public String getId() {
                            		// TODO Auto-generated method stub
                            		return null;
                            	}
                            
                            	public byte[] getAttributes() {
                            		// TODO Auto-generated method stub
                            		return null;
                            	}
                            
                            }

                            Comment


                            • #15
                              Originally posted by Misha79 View Post
                              One question -
                              what is the difference between WebFlowConversation and PersistentConversation classes. Both are custom classes and PersistentConversation implements Conversation interface. What is the need of WebFlowConversation. I just added 2 attributes-
                              Code:
                              public class WebFlowConversation {
                              
                              	public String getId() {
                              		// TODO Auto-generated method stub
                              		return null;
                              	}
                              
                              	public byte[] getAttributes() {
                              		// TODO Auto-generated method stub
                              		return null;
                              	}
                              
                              }
                              Sorry for my late reply.

                              First of all, let's talk about what both classes have in common; both WebFlowConversation and PersistentConversation represents and contains all data of conversation every conversation among different flow executions of a flow instance. The difference is in the scope each class plays its own role. In other words, WebFlowConversation is just a POJO mapped to the database table that stores the conversation data, in my case it is an JPA entity. On the other hand we have the PersistentConversation, which is created by parsing every WebFlowConversation retrieved from database and it implements org.springframework.webflow.conversation.Conversat ion, so it provides all the functionality to interact in conversations (for instance, locking and ending conversations, manage attributes, etc).

                              In the original Erwin Vervaet, one of the authors of Spring Web Flow, there is only a class which represents the database entity and at the same time implements org.springframework.webflow.conversation.Conversat ion but it was my decision to split it in two different classes because in my opinion a database entity doesn't have to know anything about web layer's matters, for example, what a SWF conversation is and how to deal with it. You are free to do it whatever the way to consider better for your scenario.

                              I hope I've solved your doubt.

                              Regards.
                              Last edited by alejandrogarciaseco; Sep 24th, 2012, 05:26 AM.

                              Comment

                              Working...
                              X