4 Replies Latest reply on Apr 2, 2007 6:54 PM by raffaele.camanzo

    Bug in explicit conversation id management in SEAM 1.2.1 (ma

    raffaele.camanzo

      This problem seems to affect both the current management of the explicit conversation id (@Begin(id=...)) and the future solution (with the declaration in the pages.xml).

      Seam 1.2.1 seems to be unable to restore the current state of a conversation in this way:

      I made three simple POJOs:

      ActionOne.java:

      @Name("actionOne")
      @Scope(ScopeType.CONVERSATION)
      public class ActionOne {
      
       private List<String> starttimes;
       private int counter = 0;
      
       @Create
       public void createit() {
       starttimes = new ArrayList<String>();
       starttimes.add("Executing ActionOne @Create method");
       }
      
      // @Begin I don't know if this is needed or not, anyway, it does not work now both if it is
      // present or not
       public String startConversation() {
       Logger.getLogger(this.getClass()).info("<ACTIONONE> START CONVERSATION METHOD CALLED");
      
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> IS THERE A LONG RUNNING CONVERSATION: " + (Conversation.instance().isLongRunning() ? "YES" : "NO"));
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> IS THERE A NESTED CONVERSATION: " + (Conversation.instance().isNested() ? "YES" : "NO"));
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> LONG RUNNING CONVERSATION ID IS: " + Conversation.instance().getId());
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> LONG RUNNING CONVERSATION VIEW ID IS: " + Conversation.instance().getViewId());
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> LONG RUNNING CONVERSATION DESCRIPTION IS: " + Conversation.instance().getDescription());
      
       return "one";
       }
      
       @End
       public String endConversation() {
       Logger.getLogger(this.getClass()).info("<ACTIONONE> END CONVERSATION METHOD CALLED");
       return "one";
       }
      
       /**
       * starttimes member Getter
       *
       * @return starttimes member
       */
       public List<String> getStarttimes() {
       return starttimes;
       }
      
       /**
       * starttimes member Setter
       *
       * @param starttimes New value for the starttimes member
       */
       public void setStarttimes(List<String> starttimes) {
       this.starttimes = starttimes;
       }
      
       public String doIt() {
       counter++;
       starttimes.add("ACTIONONE DOIT CALLED " + counter + " TIMES!");
      
      
       Logger.getLogger(this.getClass()).info("<DOIT> IS THERE A LONG RUNNING CONVERSATION: " + (Conversation.instance().isLongRunning() ? "YES" : "NO"));
       Logger.getLogger(this.getClass()).info("<DOIT> IS THERE A NESTED CONVERSATION: " + (Conversation.instance().isNested() ? "YES" : "NO"));
       Logger.getLogger(this.getClass()).info("<DOIT> LONG RUNNING CONVERSATION ID IS: " + Conversation.instance().getId());
       Logger.getLogger(this.getClass()).info("<DOIT> LONG RUNNING CONVERSATION VIEW ID IS: " + Conversation.instance().getViewId());
       Logger.getLogger(this.getClass()).info("<DOIT> LONG RUNNING CONVERSATION DESCRIPTION IS: " + Conversation.instance().getDescription());
      
       return "one";
       }
      }
      


      ActionTwo.java:
      @Name("actionTwo")
      @Scope(ScopeType.CONVERSATION)
      public class ActionTwo {
      
       private List<String> starttimes;
       private int counter = 0;
      
      
       @Create
       public void createit() {
       starttimes = new ArrayList<String>();
       starttimes.add("<ACTIONTWO> Executing ActionTwo @Create method");
       }
      
       @Begin(id="myid2")
       public String startConversation() {
       Logger.getLogger(this.getClass()).info("<ACTIONTWO> START CONVERSATION METHOD CALLED");
      
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> IS THERE A LONG RUNNING CONVERSATION: " + (Conversation.instance().isLongRunning() ? "YES" : "NO"));
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> IS THERE A NESTED CONVERSATION: " + (Conversation.instance().isNested() ? "YES" : "NO"));
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> LONG RUNNING CONVERSATION ID IS: " + Conversation.instance().getId());
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> LONG RUNNING CONVERSATION VIEW ID IS: " + Conversation.instance().getViewId());
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> LONG RUNNING CONVERSATION DESCRIPTION IS: " + Conversation.instance().getDescription());
      
       return "two";
       }
      
       @End @Remove @Destroy
       public String endConversation() {
       Logger.getLogger(this.getClass()).info("<ACTIONTWO> END CONVERSATION METHOD CALLED");
       return "two";
       }
      
       /**
       * starttimes member Getter
       *
       * @return starttimes member
       */
       public List<String> getStarttimes() {
       return starttimes;
       }
      
       /**
       * starttimes member Setter
       *
       * @param starttimes New value for the starttimes member
       */
       public void setStarttimes(List<String> starttimes) {
       this.starttimes = starttimes;
       }
      
       public String doIt() {
       counter++;
       starttimes.add("ACTIONTWO DOIT CALLED " + counter + " TIMES!");
      
       Logger.getLogger(this.getClass()).info("<DOIT> IS THERE A LONG RUNNING CONVERSATION: " + (Conversation.instance().isLongRunning() ? "YES" : "NO"));
       Logger.getLogger(this.getClass()).info("<DOIT> IS THERE A NESTED CONVERSATION: " + (Conversation.instance().isNested() ? "YES" : "NO"));
       Logger.getLogger(this.getClass()).info("<DOIT> LONG RUNNING CONVERSATION ID IS: " + Conversation.instance().getId());
       Logger.getLogger(this.getClass()).info("<DOIT> LONG RUNNING CONVERSATION VIEW ID IS: " + Conversation.instance().getViewId());
       Logger.getLogger(this.getClass()).info("<DOIT> LONG RUNNING CONVERSATION DESCRIPTION IS: " + Conversation.instance().getDescription());
      
       return "two";
       }
      
      
      }
      


      ActionThree.java:
      @Name("actionThree")
      @Scope(ScopeType.CONVERSATION)
      public class ActionThree {
      
       private List<String> starttimes;
       private int counter = 0;
      
       @Begin
       public String startConversation() {
       Logger.getLogger(this.getClass()).info("<ACTIONTHREE> START CONVERSATION METHOD CALLED");
       starttimes = new ArrayList<String>();
       starttimes.add("Executing ActionThree @Begin method");
      
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> IS THERE A LONG RUNNING CONVERSATION: " + (Conversation.instance().isLongRunning() ? "YES" : "NO"));
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> IS THERE A NESTED CONVERSATION: " + (Conversation.instance().isNested() ? "YES" : "NO"));
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> LONG RUNNING CONVERSATION ID IS: " + Conversation.instance().getId());
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> LONG RUNNING CONVERSATION VIEW ID IS: " + Conversation.instance().getViewId());
       Logger.getLogger(this.getClass()).info("<START CONVERSATION> LONG RUNNING CONVERSATION DESCRIPTION IS: " + Conversation.instance().getDescription());
      
       return "three";
       }
      
       @End
       public String endConversation() {
       Logger.getLogger(this.getClass()).info("<ACTIONTHREE> END CONVERSATION METHOD CALLED");
       return "three";
       }
      
       /**
       * starttimes member Getter
       *
       * @return starttimes member
       */
       public List<String> getStarttimes() {
       return starttimes;
       }
      
       /**
       * starttimes member Setter
       *
       * @param starttimes New value for the starttimes member
       */
       public void setStarttimes(List<String> starttimes) {
       this.starttimes = starttimes;
       }
      
       public String doIt() {
       counter++;
       starttimes.add("ACTIONTHREE DOIT CALLED " + counter + " TIMES!");
      
       Logger.getLogger(this.getClass()).info("<DOIT> IS THERE A LONG RUNNING CONVERSATION: " + (Conversation.instance().isLongRunning() ? "YES" : "NO"));
       Logger.getLogger(this.getClass()).info("<DOIT> IS THERE A NESTED CONVERSATION: " + (Conversation.instance().isNested() ? "YES" : "NO"));
       Logger.getLogger(this.getClass()).info("<DOIT> LONG RUNNING CONVERSATION ID IS: " + Conversation.instance().getId());
       Logger.getLogger(this.getClass()).info("<DOIT> LONG RUNNING CONVERSATION VIEW ID IS: " + Conversation.instance().getViewId());
       Logger.getLogger(this.getClass()).info("<DOIT> LONG RUNNING CONVERSATION DESCRIPTION IS: " + Conversation.instance().getDescription());
      
       return "three";
       }
      
      
      }
      


      I configured the pages.xml in order to support the next explicit conversation id feature:

      pages.xml
      <!DOCTYPE pages PUBLIC
       "-//JBoss/Seam Pages Configuration DTD 1.1//EN"
       "http://jboss.com/products/seam/pages-1.2.dtd">
      
      <pages>
       <conversation name="convOne" parameter-name="aname" parameter-value="#{sessionHandler.seqOne}" />
      
       <page view-id="/actionone.xhtml" conversation="convOne">
       </page>
      
      </pages>
      


      the seqOne method simply returns a string with an incremental value postfix.

      and added three links to call them all:
       <div id="control">
       <h:form>
       <table>
       <tr>
       <td>
       <h:outputText value="Press the button to call the @Begin action of the actionOne conversation" />
       </td>
       </tr>
       <tr>
       <td>
       <h:commandLink action="#{actionOne.startConversation}" value="Start Conversation with pages conversation definition">
       <s:conversationPropagation type="none" />
       </h:commandLink>
       </td>
       </tr>
       </table>
       </h:form>
       <h:form>
       <table>
       <tr>
       <td>
       <h:outputText value="Press the button to call the @Begin action of the actionTwo conversation" />
       </td>
       </tr>
       <tr>
       <td>
       <h:commandLink action="#{actionTwo.startConversation}" value="Start Conversation with static @Begin(id='myId2')">
       <s:conversationPropagation type="none" />
       </h:commandLink>
       </td>
       </tr>
       </table>
       </h:form>
       <h:form>
       <table>
       <tr>
       <td>
       <h:outputText value="Press the button to call the @Begin action of the actionThree conversation" />
       </td>
       </tr>
       <tr>
       <td>
       <h:commandLink action="#{actionThree.startConversation}" value="Start Conversation with generated id">
       <s:conversationPropagation type="none" />
       </h:commandLink>
       </td>
       </tr>
       </table>
       </h:form>
       </div>
      


      disabling conversation propagation in order to avoid to ask for the LRC switch.

      the three xhtml pages related to the components are almost identical and trivial:
      <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
       xmlns:ui="http://java.sun.com/jsf/facelets"
       xmlns:h="http://java.sun.com/jsf/html"
       xmlns:f="http://java.sun.com/jsf/core"
       xmlns:s="http://jboss.com/products/seam/taglib"
       xmlns:c="http://java.sun.com/jstl/core"
       template="main.xhtml">
      
      <!-- context manager -->
      <ui:define name="actioncontent">
       <h:dataTable value="#{actionThree.starttimes}" var="iter">
       <h:column>
       <h:outputText value="#{iter}" />
       </h:column>
       </h:dataTable>
      
       <h:form>
       <s:link action="#{actionThree.doIt}">
       <h:outputText value="Press me I should run into #{conversation.id} conversation" />
       </s:link>
       </h:form>
      </ui:define>
      
      </ui:composition>
      


      Basically what I expect to obtain is this:
      Clicking on one of the three links I start a conversation, then, clicking on the doIt link see the list of strings to grow; unfortunately this is true only in the third case (when the id is generated by Seam), both in the first and in the second Seam seems to not bind the component to the conversation, indeed, running test on the component instance two, when I click on the doIt link, I read in the log this print:

       DEBUG [org.jboss.seam.core.Manager] Found conversation id in request parameter: myid2
       DEBUG [org.jboss.seam.core.Manager] Restoring conversation with id: myid2
       DEBUG [org.jboss.seam.jsf.AbstractSeamPhaseListener] After restoring conversation context: ConversationContext(myid2)
      


      but the list does not grow, moreover if I create the list of strings in the @Begin method (as I do in the third example) the doIt method fails with a NullPointerException because the list is not present.

      That's it. Is this a bug?

      Two questions on conversation management:
      - Can I expect that if I click say on the second link (the static id.. not correct but to understand) Seam will understand that I'm asking for that conversation and resumes it?
      - Is it legal to use the Redirect API knowing the conversation id (regardless generated or explicit) in order to redirect to that conversation?
      Both the questions without a LRC in place (s:conversationPropagation type="none")

      Regards,
      Raffaele Camanzo


        • 1. Re: Bug in explicit conversation id management in SEAM 1.2.1
          gavin.king

          Huh???!

          You have defined this page to use a natural conversation id, and then you try to use other strategies *on the same page*??

          I would not expect that to work either. Why should it?

          • 2. Re: Bug in explicit conversation id management in SEAM 1.2.1
            raffaele.camanzo

             


            You have defined this page to use a natural conversation id, and then you try to use other strategies *on the same page*??


            Definitely no. Three Seam components, three pages, three navigation rules, to avoid any misunderstanding I created three separate projects too and made the tests again.
            One project defines the conversation id in a natural way, another one defines it in the old fashion and the third one gets a generated conversation id.
            The three projects have the same behaviour:
            One link from the main page to the @Begin action of the conversation, from that link I move through the navigation rules in another page, the one related to that conversation.

            This is the target page:
            <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
            <ui:composition xmlns="http://www.w3.org/1999/xhtml"
             xmlns:ui="http://java.sun.com/jsf/facelets"
             xmlns:h="http://java.sun.com/jsf/html"
             xmlns:f="http://java.sun.com/jsf/core"
             xmlns:s="http://jboss.com/products/seam/taglib"
             xmlns:c="http://java.sun.com/jstl/core"
             template="template.xhtml">
            
            <!-- context manager -->
            <ui:define name="actioncontent">
             <h:dataTable value="#{actionTwo.starttimes}" var="iter">
             <h:column>
             <h:outputText value="#{iter}" />
             </h:column>
             </h:dataTable>
            
             <h:form>
             <s:link action="#{actionTwo.doIt}">
             <h:outputText value="Press me I should run into #{conversation.id} conversation" />
             </s:link>
             </h:form>
            </ui:define>
            
            </ui:composition>
            


            The s:link refers to an action of the Seam component actionTwo, such action simply adds a string to a list, I guess I'm in a LRC, the @Begin method of the actionTwo component returned the outcome of this page, the s:link propagate the conversation, then I expect that Seam holds the component (saved in the conversation context) in the current conversation, but this does not happen in the two test cases with explicit conversation id.
            In the Seam-assigned conversation id test everything works properly.

            I can post you both the test cases and a more detailed description, anyway, I don't think you need server logs of Seam.

            Can you please answer to these two questions?


            - Can I expect that if I click twice on a link connected to the start conversation of an explicit id conversation Seam will understand that I'm asking for that conversation and resumes it?
            - Is it legal to use the Redirect API knowing the conversation id (regardless generated or explicit) in order to resume a conversation?

            Both the questions without a LRC in place (s:conversationPropagation type="none")


            Actually my application does not work with Seam 1.2.1, I'd like to move to a natural conversation id compliant usage and I want to avoid any misunderstanding of the framework.

            Regards,
            Raffaele Camanzo



            • 3. Re: Bug in explicit conversation id management in SEAM 1.2.1
              raffaele.camanzo

              It made me crazy but I found it. It was a problem with my configuration faces-config.xml / pages.xml.
              I was doing almost the same thing of the booking sample (booking confirmation) and there I found the solution: I moved the navigation rules in the pages and (I think this is the reason) redirected the requests.

              But I'm facing another problem (or I'm doing something else wrong):

              In the booking sample language (meaningless for the sample but to show the scenario):
              I would like to include the hotel search result list in the template (having it visible also in the booking page) and I would like to select another hotel from the booking, and then start the new conversation having the same view-id, same action but using hotel.id to create different conversation id, through the conversation-param EL).

              To achieve this I blocked the conversation propagation in the hotel selection <s:link>, something like this:

              <s:link action="#{myaction.begin}" value="Select It">
               <s:conversationPropagation type="none" />
              </s:link>
              


              and configured the pages.xml as follows:

              <pages no-conversation-view-id="/main.xhtml">
               <conversation name="convOne" parameter-name="myactionId" parameter-value="#{myhotel.id}" />
              
               <page view-id="/main.xhtml">
               <navigation from-action="#{myaction.begin}">
              <!-- myaction begin method does not have @Begin annotation -->
               <begin-conversation/>
               <redirect view-id="/myaction.xhtml" />
               </navigation>
               </page>
              
               <page view-id="/myaction.xhtml">
               <navigation from-action="#{myaction.begin}">
               <begin-conversation/>
               <redirect />
               </navigation>
               </page>
              
              </pages>
              


              The effect is this:
              - the first time everything is ok, conversation started, promoted as a long running and so on...
              - when I try to book more than once Seam is unable to recognize (does not evaluate the EL #{myhotel.id}) and assigns a generated id but starts the conversation, promotes it as a long running...

              Can I achieve this with explicit natural conversation id? Am I doing something wrong or is a bug?

              Regards,
              Raffaele Camanzo


              • 4. Re: Bug in explicit conversation id management in SEAM 1.2.1
                raffaele.camanzo

                Errata corrige (I tried a modification and left the page definition without conversation="")

                the pages.xml is a little bit different:

                ...
                 <page view-id="/myaction.xhtml" conversation="convOne">
                 <navigation from-action="#{myaction.begin}">
                 <begin-conversation/>
                 <redirect />
                 </navigation>
                 </page>
                ...
                


                also the effect is different:
                - the first ok
                - the second time it does not render the page (comes back to the main.xhtml)
                - the third ok
                ...

                in the even calls Seam intercepts (through the RootInterceptor) the parameter-value EL expression, evaluates it and creates the correct (with the correct conversation id) url to redirect to but neither redirects to the myaction page nor with that conversation id.

                Regards,
                Raffaele Camanzo