1 2 3 Previous Next 32 Replies Latest reply on Nov 5, 2007 7:36 AM by wschwendt

    Feature request regarding nested conversations

    wschwendt

      to all the Seam committers and experts here on this forum:

      I have a feature request for Seam which I think would be very useful. It isn't any idea conceived by me. Rather, it's a feature whose value has been proven already by Spring Webflow.

      I'm not sure whether there is sufficient interest among the Seam user community and what the Seam leaders think about it. One problem is that unfortunately Gavin King appears to be no longer present on this forum (yes, I understand that he's probably awfully busy and cannot answer mundane support questions here). But without his support and his implementation, the feature won't probably find its way into Seam.


      As I don't know whether there is interest, I just provide a short description for now. And please excuse my English:

      Nested conversations could be a powerful mechanism to enable the definition of complex flows composed of one or more subflows. However, they are somewhat limited if there is no communication at all between the parent conversation (the calling flow) and the nested conversation (the subflow). With Seam it is easily possible to transfer state from a parent conversation to a nested conversation. It is much more difficult however to transfer state (which represents an ending result) back to the parent conversation when the nested conversation ends.


      For example, when specifying navigation rules in pages.xml, we can use <begin-conversation nested="true"/> to start a new nested conversation and
      <pages:out name="contextVar" value="#{ELexpr}"/> to initialize a context variable in the scope of the new nested conversation, where #{ELexpr} could freely reference context variables from the parent conversation scope.


      But transferring an ending result back to the parent conversation, when the nested conversation has ended, is difficult. There is unfortunately no elegant way to do this with Seam. As long as the nested conversation lasts, context variables of the outer conversation can only be read but not written to. And the following approach is not viable either: We cannot simply access a component from the parent conversation and call a property Setter of this component with the ending result of the nested conversation supplied as argument for this Setter. The reason is that when a component from the parent conversation is called while the nested conversation is still in progress, the ManagedEntityIdentityInterceptor saves wrappers for the called component in the scope of the nested conversation (!) and not the scope of the parent conversation. So once the nested conversation scope gets destroyed at the end of the nested conversation, the saved wrappers for the component from the parent conversation get destroyed as well.

      Of course, with some ugly hacks it is possible to transfer state at the end of the nested conversation back to the parent conversation. But what would be useful is an elegant solution, not an ugly hack.



      Spring Web flow supports the concept of an "attribute-mapper". The attribute-mapper plays a significant role when calling subflows, providing the integration between a parent flow and a child flow. In Spring Webflow any information the parent flow needs to pass into the subflow, or the subflow needs to return back to the parent, must be explicitly mapped.

      Spring Webflow example:
      
      <subflow-state id="enterShippingInformation" flow="shipping-flow">
       <attribute-mapper>
       <input-mapping name="purchase.requiresShipping" as="requiresShipping"/>
       <output-mapping name="shipping" as="purchase.shipping"/>
       </attribute-mapper>
       <transition on="end" to="placeOrder"/>
      </subflow-state>
      
      
      // The <input-mapping> element instructs Spring Web Flow to map
      // the value of the requiresShipping property of the purchase object in the
      // parent flow scope to an attribute in the subflow scope with the name
      // requiresShipping. The <output-mapping> element instructs Spring Web
      // Flow to map the result of the shipping subflow
      


      For Seam, we don't need an <input-mapping> for the direction from the parent conversation to the nested conversation. As mentioned above, we can easily transfer state in this direction. But an <output-mapping> would be highly desirable. It could be a simplified output-mapper to avoid problems with the ManagedEntityIdentityInterceptor. Much better than nothing at all.

      For example something like this

      // pages.xml
      
      <end-conversation before-redirect="true">
       <output-mapping from="shipping" to="subFlowResult"/>
       // the output-mapping instructs Seam at the end of the
       // nested conversation to map the value of context variable
       // named "shipping" in the scope of the nested conversation
       // to a context var named "subFlowResult" in the parent
       // conversation. For the sake of simplicity,
       // we just map simple context variables, not EL path expressions.
      
       <output-mapping from="..." to="..."/>
       // another output mapping
      
       <action execute="#{bean.method}"/>
       // an action which gets executed in the scope of the parent
       // conversation after the nested conversation has
       // ended and before the redirect is done.
       // It could serve to process the outjections to the parent conversation.
      </end-conversation>
      
      



      I'm puzzled by the fact that Seam offers nothing yet in this regard. A feature like that would be highly useful when modeling complex flows and composing them of one or more subflows. I hope that an output-mapper gets added to Seam, in order to avoid having to resort to ugly hacks such as writing from the nested conversation to the parent conversation scope.


        • 1. Re: Feature request regarding nested conversations

          wschwendt,

          I completely understand your problem, but it seems that most of the posts I see about nested conversations are somewhat puzzling to me so I would like to hear some opinions on their usage.

          I have been using nested conversations in situations where I require a continuation server implementation. After a lot of thought, it seemed that this was the most logical usage of nested conversations given their state snapshot behavior. I have successfully implemented some very complex page flows with master-details editing relationships and all issues with the back button have been alleviated. The user is free to navigate as they please!

          The main issue that comes in with conversations is the master-details editing where the master controls the persistence of the data. For example, a Person and their Addresses:

          1. Select Person for editing which displays a Person edit screen
          2. Select Add Addresses which forwards to a secondary screen
          3. Addresses are added and the screen is submitted which updates the in-memory Person
          4. The Person edit screen is redisplayed with the Addresses added
          5. The user then uses the back button to return to the original Person edit screen and submits which persists Person with new addresses

          If a single long-running conversation was used, you end up submitting addresses that were not intended by the user (at least not what they saw on the browser-cached edit screen) since the Person was updated in the conversation context. If the conversation is nested for the Address details screen, the addition of addresses does not affect the outer conversation. In other words, we have a snapshot of state prior to adding the Addresses.

          We do NOT end this conversation when the Addresses are submitted. Instead we continue the nested conversation on the new Person edit screen. Now, if the user was to back up to the original Person edit screen, the snapshot of conversation state is restored (the outer conversation) unaffected by the user's actions. If the user then submits, the state is persisted consistent with what the user "sees."

          By updating the outer (parent) conversation backing up to the original Person edit screen has the same effect as using a single long-running conversation. Data is persisted that the user did not intend. This is because we no longer have a snapshot of the browser cached state at that time.

          I hope this makes sense. If not, I can clarify. Thanks.

          ...

          By the way, a workaround in regards to updating parent conversation state you can always use a non-Seam POJO which maintains mutable state. Any class not annotated with @Name, will not be managed by Seam and thus, will not be reverted back to the outer conversation state when a nested conversation ends. All changes to the internal state of the object will stick in the outer conversation.

          • 2. Re: Feature request regarding nested conversations
            wschwendt

            A great thank your for writing a response, Jacob! I already felt that most users on this forum aren't interested and don't care about the feature request, even though it's a feature not freshly conceived by me, but a feature which is already available and proven in Spring Web Flow.

            I'll read your post later this evening and then think about your use of nested conversations (the master-detail editing). Perhaps I'll understand then why the output-mapper function still misses in Seam.


            PS: Your workaround for transferring state to the parent conversation state, with a non-Seam POJO placed in parent conversation scope, is smart. One of the simple workarounds I tested was to "fake" a write access to the parent conversation at the end of a nested conversation.
            This can be done, given that the underlying store for the Conversation context is the Session scope (entries for a certain conversation are stored with a special generated key that includes the conversation id). If we know the format of this key and generate this key ourselves, we can write to the parent conversation scope. It should only be kept in mind that by doing this we completely bypass Seam's support for Entity passivation (when an entity instance or a collection of entity instances is regularly saved in conversation scope, ie. not with the described fake write, Seam wraps the Entity instance into a special wrapper class such as org.jboss.seam.contexts.EntityBean or org.jboss.seam.contexts.EntityBeanList and then saves this wrapper class instance).

            • 3. Re: Feature request regarding nested conversations
              wschwendt

               

              "wschwendt" wrote:
              A great thank your for writing a response, Jacob!


              Sorry, I have to sell an 'r'. It should read:

              A great thank you_ for writing a response, Jacob!

              • 4. Re: Feature request regarding nested conversations

                No problem. I would welcome any input from other forum members as well.

                • 5. Re: Feature request regarding nested conversations
                  matt.drees

                   

                  "jacob.orshalick" wrote:

                  If a single long-running conversation was used, you end up submitting addresses that were not intended by the user (at least not what they saw on the browser-cached edit screen) since the Person was updated in the conversation context. If the conversation is nested for the Address details screen, the addition of addresses does not affect the outer conversation. In other words, we have a snapshot of state prior to adding the Addresses.


                  Hi Jacob,

                  I wonder if you could explain this a little more; I don't completely follow. It seems to me that if you have a (managed) Person entity in the parent conversation, and (in a nested conversation) you add an address to it (something like person.addAddress(newAddress) ), the newAddress will not be directly in the parent conversation, but it will be directly referenced by Person, which is in the parent conversation. And thus entityManager.flush() would cause newAddress to be persisted, even if the user backbuttons out of the nested conversation.

                  So, I'm guessing you're not doing a person.addAddress(newAddress) kind of thing. What are you doing?

                  Thanks for the posts, guys! I think nested conversations need to be discussed more.

                  • 6. Re: Feature request regarding nested conversations

                    Hi Matt,

                    Thanks for the response!

                    Actually I am doing a Person.setAddresses(listOfAddresses) when the Address details page is submitted. This sets the list of addresses for the Person in the nested conversation not affecting the parent conversation. The reason is that Seam has taken a snapshot of my Person entity in the outer conversation on nesting (which didn't have the new addresses). The original Person instance will be reverted back to should the user back up to the original edit Person screen and submit. Seam retrieves the outer conversation by id on submit and restores the state of my Person object at the time of the snapshot which doesn't include the new addresses.

                    I hope that clarifies the scenario a little bit...

                    You bring up an interesting point with respect to the EntityManager. I will have to test this point using the Seam managed persistence context. I do not know the effects of nesting on the Seam managed persistence context and whether it is included in the state snapshot. Currently my implementation does not require a Seam managed persistence context as we always perform a fetch join when dependent objects are needed and merge() on final submit.

                    Thus, I am keeping my entities within the scope of the conversation context. This ensures that nesting the conversation will maintain a state snapshot of my entity in the outer conversation context regardless of changes to the entity in the nested conversation.

                    Maybe an expert on the effects of nested conversations on a Seam managed persistence context could shed some light on this?

                    • 7. Seam-managed persistence context
                      wschwendt

                       

                      "matt.drees" wrote:

                      I think nested conversations need to be discussed more.


                      I have the same opinion.


                      "jacob.orshalick" wrote:
                      Maybe an expert on the effects of nested conversations on a Seam managed persistence context could shed some light on this?


                      I'm anything but an expert regarding the effects of nested conversations on a Seam-managed persistence context. So far my understanding (could be wrong) is that a Seam-managed persistence context corresponds to
                      an instance of the org.jboss.seam.persistence.ManagedPersistenceContext component. This is a conversation-scoped component, hence it can be injected into other components using the @In annotation. Unlike "normal" conversation-scoped components, ManagedPersistenceContext is annotated with @BypassInterceptors and @Install(false) however. To define a managed persistence context with the name "entityManager", the typical idiom is that the framework user puts a component definition in config-file components.xml

                       // components.xml
                      
                       <persistence:managed-persistence-context
                       name="entityManager"
                       persistence-unit-jndi-name="java:/contactlistEntityManagerFactory"/>
                      


                      How does a Seam-managed persistence context get instantiated? It gets instantiated like any ordinary Seam component. For example, the Seam CRUD framework classes EntityHome and EntityQuery both extend
                      org.jboss.seam.framework.PersistenceController which creates a Seam-managed persistence context effectively via a call of Component.getInstance(name);

                      Now what happens if a nested conversation is in progress and there is an injection such as the following or another call of Component.getInstance("entityManager")?

                      @In EntityManager entityManager; //a Seam-managed persistence context
                      


                      I'd say the usual rules for injections apply, that means Seam tries a hierarchical context search. A nested conversation has read-only access to the context of the outer conversation. Therefore, if a Seam-managed persistence context was instantiated already in the parent conversation, the hierarchical search finds it and the nested conversation thus shares the same persistence context with the parent conversation.



                      • 8. Re: Feature request regarding nested conversations

                        wschwendt,

                        Thanks for the information. Yes, I understand how the Seam managed persistence context works, in theory. I suppose my question relates more to how the Seam managed persistence context maintains managed entities with respect to nested conversations...

                        So if I have an entity in my outer conversation, say Person as before. I loaded my Person instance in the outer conversation with a Seam managed persistence context which means my Person instance remains managed for the course of the conversation. Now I navigate to the Address details page which nests a conversation,

                        1. Does the snapshot of the Conversation context include a snapshot of the *managed entity*?
                        2. If I make changes to the Person in the nested conversation, since this is a *managed entity* does it make any difference in the semantics of the outer vs. nested conversation?
                        3. Will I still be able to revert back to the outer conversation snapshot of my Person object as I can with a detached entity?

                        I will have to create an example that tests this behavior with attached entities as I know it works with detached entities. If anyone could provide any insight, that would be great. Thanks again.

                        • 9. Re: Feature request regarding nested conversations
                          wschwendt

                          Jacob, as for my previous post, I wrote it as some sort of summary a further discussion can be based on. This also allows other readers to follow this thread more easily.

                          As for your other questions, I have to think about some things first before I can write a follow-up post that tries to address these other issues.

                          Regards, Wolfgang

                          • 10. Re: Seam-managed persistence context
                            wschwendt

                             

                            "wschwendt" wrote:

                            So far my understanding is that a Seam-managed persistence context corresponds to an instance of the org.jboss.seam.persistence.ManagedPersistenceContext component. This is a conversation-scoped component, hence it can be injected into other components using the @In annotation.


                            I don't like my previous wording here and want to correct it. While it's true that a Seam-managed persistence context corresponds to an instance of org.jboss.seam.persistence.ManagedPersistenceContext component, it's not true that an instance of ManagedPersistenceContext is injected into other components when there is an annotation such as @In EntityManager entityManager. Rather, the ManagedPersistenceContext acts a manager component for an EntityManager; the method ManagedPersistenceContext.getEntityManager() is annotated with @Unwrap. It is the return value of the @Unwrap method that gets injected when a ManagedPersistenceContext component is referenced.

                            • 11. More about nested conversations
                              wschwendt

                              I looked again at the Seam reference documentation, section "6.2. Nested conversations"

                              It cites correct behavior in the face of backbuttoning and workspace management as motives for nested conversations.


                              "Seam Reference 6.2" wrote:
                              A conversation may be thought of as a continuable state. Nested conversations allow the application to capture a consistent continuable state at various points in a user interaction, thus insuring truly correct behavior in the face of backbuttoning and workspace management.

                              TODO: an example to show how a nested conversation prevents bad stuff happening when you backbutton.


                              Using nested conversation to alleviate problems caused by the back button is exactly what you described in your post, Jacob (post #2 in this thread).

                              On the other hand, if I think of breadcrumbs and the conversation stack (-> 6.7.5. Breadcrumbs), the use of nested conversations goes into a direction much similar to the composition of "subflows" my post #1 in this thread is related to.


                              [More to follow]


                              • 12. Re: Feature request regarding nested conversations

                                 

                                if I think of breadcrumbs and the conversation stack (-> 6.7.5. Breadcrumbs), the use of nested conversations goes into a direction much similar to the composition of "subflows"


                                This is a good point, and I have discussed this at length with my colleagues. But, if you think of nested conversations as continuations (http://www.brainbell.com/tutorials/java/Continuation_Servers.htm), the implementation of the Seam conversation model suddenly makes perfect sense. I actually posted the breadcrumb issue you raise as a topic:

                                http://www.jboss.org/index.html?module=bb&op=viewtopic&t=118894

                                Actually, simply filtering the ConversationStack by view-id yields the expected results from breadcrumbs with the continuation view of nested conversations. I can post the filtering code if you or anyone is interested. It is simply a component that wraps the ConversationStack.

                                I will soon be posting an extended seam-booking example with an accompanying article that will illustrate the continuation approach in-depth.

                                • 13. Re: Feature request regarding nested conversations
                                  matt.drees

                                  By the way, I didn't realize this:

                                  "wschwendt" wrote:
                                  And the following approach is not viable either: We cannot simply access a component from the parent conversation and call a property Setter of this component with the ending result of the nested conversation supplied as argument for this Setter. The reason is that when a component from the parent conversation is called while the nested conversation is still in progress, the ManagedEntityIdentityInterceptor saves wrappers for the called component in the scope of the nested conversation (!) and not the scope of the parent conversation. So once the nested conversation scope gets destroyed at the end of the nested conversation, the saved wrappers for the component from the parent conversation get destroyed as well.



                                  I can't help but think this wasn't intended; I'm guessing ManagedEntityIdentityInterceptor is intended to be transparent. (Side rant: it's not transparent, and has caused me some pain.)

                                  • 14. Re: Feature request regarding nested conversations
                                    baz

                                    Very interesting thread.
                                    i like to give a new usecase (or workflow)
                                    We have a search screen, where the user can search for an object(e.g. an experiment)
                                    When navigation to the show/edit screen ocurs a conversation is started and the object(the experiment) is shown.

                                    Now the user can work with the object(the experiment)

                                    What comes in now is a subworkflow. This workflow uses a nested conversation and could be started on any page. When this subworkflow ends, the nested conversation ends and redirection to the starting page occurs.

                                    Now imagine that our object(experiment) consists of many parts (analysis,mtplate,samples and so on). Could it be good design to create some of this parts in a subworkflow? And if so, how can i get hold of the assembled object in my outer conversation?

                                    This gets more complex when you are aiming to an object hierarchy
                                    An experiment has mtplates and analyses. Now when editing an analysis, this analysis has to be associated with an mtplate. But if none exist, it must be created. After creation of the mtplate there is no connection between analysis and the created mtplate because the mtplate belongs to the experiment.
                                    I hope you see my point.
                                    My question is how this kind of workflows can be correctly implemented with seam and conversations? For now, it seems to me that it is nessacary to deliver results from the nested conversation to the outer conversation.

                                    Ciao,
                                    Carsten

                                    1 2 3 Previous Next