5 Replies Latest reply on Oct 24, 2009 9:01 PM by Brian Wallace

    Long Running Conversation Question

    Brian Wallace Newbie

      I am having an issue with a new long running conversation being created when I don't expect it to be.  I am using Seam 2.2.0.GA running on Glassfish 2.1.  My example is as follows:


      I have two pages.  The first is a login page.  Once the user has successfully authenticated the application redirects to second page called identify-feature.  The identify-feature page contains a input text box, a command button (used for validating the data entered in the input text box), and a submit button which becomes enabled after the data entered in the input text box is validated.  When the identify-feature page is initially rendered a new long running conversation is created as I expect.  What I don't understand is why a second long running conversation is created when the validate command button is pressed and the identify-feature page is re-rendered.


      My pages.xml looks as follows:


      <?xml version="1.0" encoding="UTF-8"?>
      <pages xmlns="http://jboss.com/products/seam/pages" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.0.xsd"
          no-conversation-view-id="/login.xhtml" login-view-id="/login.xhtml"
      >
      
          <page view-id="*">
              <navigation from-action="#{identity.logout}">
                  <end-conversation />
                  <redirect view-id="/login.xhtml" />
              </navigation>
          </page>
      
          <page view-id="/login.xhtml" action="#{identity.isLoggedIn}">
      
              <navigation from-action="#{identity.isLoggedIn}">
                  <rule if-outcome="true">
                      <redirect view-id="/identify-feature.xhtml" />
                  </rule>
              </navigation>
      
              <navigation>
                  <rule if="#{identity.loggedIn}">
                      <redirect view-id="/identify-feature.xhtml" />
                  </rule>
              </navigation>
          </page>
      
          <page view-id="/identify-feature.xhtml" login-required="true" >
              <begin-conversation join="true"/>
          </page>
      </pages>



      The identify-feature.xhtml is defined as follows:


      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
           xmlns:ui="http://java.sun.com/jsf/facelets"
           xmlns:f="http://java.sun.com/jsf/core"
           xmlns:h="http://java.sun.com/jsf/html"
           xmlns:s="http://jboss.com/products/seam/taglib">
      <body>
      
      <ui:composition template="/template.xhtml">
      
           <ui:define name="title">#{msgs.identifyFeatureStep}</ui:define>
      
           <ui:define name="contents">
                <h:form id="identifyFeature">
                     <h:panelGroup styleClass="formInput">
                          <h:outputLabel id="featureNumberLabel" for="feature"
                               value="#{msgs.featureNumber}" styleClass="formSectionTitle" />
                          <h:inputText id="feature" value="#{featurePageBean.featureNumber}"
                               tabindex="0" required="true" validator="#{featurePageBean.validateFeature}"
                               converterMessage="#{msgs.featureNumberError}"
                               requiredMessage="#{msgs.featureRequired}">
                               <f:validateLongRange minimum="1" maximum="9999999" />
                          </h:inputText>
                          <h:commandButton value="#{msgs.validate}" type="submit" />
                          <h:message styleClass="errorMsg" for="feature" />
                     </h:panelGroup>
                </h:form>
                <br />
                <h:form id="featureButtons">
                     <div class="wizardButtons"><h:commandButton
                          value="#{msgs.next}" type="submit" action="next"
                          disabled="#{feature == null}" /></div>
                </h:form>
           </ui:define>
      </ui:composition>
      </body>
      </html>
      



      The declaration of the FeaturePageBean is as follows:




      @Name("featurePageBean")
      @Scope(ScopeType.CONVERSATION)
      public class FeaturePageBean implements Serializable



      I would appreciate any assistance/direction/explanation you could provide.

        • 1. Re: Long Running Conversation Question
          Arbi Sookazian Master

          In a Seam app, there can only be one active LRC per tab/window (that's why you must specify join=true).  If there are multiple LRCs, then only one can be active and the rest are background LRCs which may eventually timeout.


          So it may be that your original LRC is being demoted to temporary conversation and then another LRC is started unless you actually see two LRCs in the debug.seam page.  Post all your code for the FeaturePageBean.  Do you have an @End anywhere?


          btw, this:

          <h:commandButton value="#{msgs.next}" type="submit" action="next" disabled="#{feature == null}" />

          is strange because the action attribute typically has EL expression in it so that an action handler method in a backing bean will be executed when user clicks the commandButton.  Also, type is most likely submit by default so you don't need to specify that.


          HTH.

          • 2. Re: Long Running Conversation Question
            Arbi Sookazian Master

            How do you know that a second long running conversation is created when the validate command button is pressed and the identify-feature page is re-rendered?

            • 3. Re: Long Running Conversation Question
              Brian Wallace Newbie

              Arbi, thank you for the responses.  It might help if I give some context to what I am trying to do.  I am working on learning JSF, Facelets, Seam, and RichFaces.  I have a small use case that lends itself very well to a multi-page wizard.  At first, I implemented the use case using JSF with JSPs.  Once I had that working I switched from using JSPs to using Facelets.  Now that I have that working I am working on incorporating Seam.  Once I have that working I will add some AJAX capabilities using RichFaces.


              At this point, I am only working with two pages of the wizard, the login page and the identify-feature page.  A conversation is probably overkill for this use case but I thought it would help me learn the mechanics.  The only way I have for the user to end the conversation is to click the logout link which is defined in the template.xhtml.  You can see the navigation rule defined in the pages.xml that has <end-conversation />.


              The way I detect there are two LRCs is that after I successfully login I view the seam.debug page in a separate tab.  Upon the initial display of the identify-feature page I can see one LRC.  After I click the validate button (which returns back to the identify-feature page) then I refresh the debug.seam page and then there are two LRCs listed, both with a view-id of /identify-feature.xhtml. 


              I have sinced re-organized the pages.xml a little.  Instead of using the <begin-conversation> element within the <page> definition of the identify-feature page I have moved it to the <navigation> definition with a from-action="#{identity.login}" like this:


              <page view-id="*">
                      <navigation from-action="#{identity.login}">
                          <rule if-outcome="loggedIn">
                              <begin-conversation join="true" />
                              <redirect view-id="/identify-feature.xhtml" />
                          </rule>
                      </navigation>
                      <navigation from-action="#{identity.logout}">
                          <end-conversation />
                          <redirect view-id="/login.xhtml" />
                      </navigation>
              </page>



              Now, I only ever have one LRC (when looking in the seam.debug page).  However, when I click the validate button on the identify-feature page, it is not participating in the LRC.  I believe this because if I add the @Conversational annotation to the FeaturePageBean.validateFeature method it throws an exception.  I have also tried removing the @Conversational annotation and using the following code from within the FeaturePageBean.validateFeature method:


              Conversation conversation = Conversation.instance();
              Boolean longRunning = conversation.isLongRunning();
              String id = conversation.getId();
              



              longRunning is false and id does not match the id of the long running conversation from the seam.debug page.


              I would be happy to zip up my code project and send it in email if it would help.  I have been reading various Seam references, including Seam In Action. Obviously, there is something I am not understanding about propagating conversations. 

              • 4. Re: Long Running Conversation Question
                Brian Wallace Newbie

                The rest of the FeaturePageBean is as follows:




                
                "@Name("featurePageBean")
                public class FeaturePageBean implements Serializable {
                
                    @In(required = false)
                    @Out(required = false, scope = ScopeType.CONVERSATION)
                    private Feature feature;
                
                    @In(required = true)
                    private User currentUser;
                
                    /**
                     * Constructor for the FeaturePageBean class.
                     */
                    public FeaturePageBean() {
                    }
                
                    /**
                     * @return the feature number of the currently validated Feature. If a valid feature has not
                     *         been loaded, a value of 0 will be returned.
                     */
                    public Integer getFeatureNumber() {
                        if (this.feature == null) {
                            return 0;
                        }
                
                        return this.feature.getNumber();
                    }
                
                    /**
                     * @param featureNumber
                     */
                    public void setFeatureNumber(Integer featureNumber) {
                        // Do nothing. Needed for having a writable property for the EL expression in the facelet page
                    }
                
                    /**
                     * Ensure the feature number entered by the user is a valid number and the user has permission
                     * to associate artifacts with the feature.
                     * 
                     * @param context
                     * @param toValidate
                     * @param value
                     */
                    public void validateFeature(FacesContext context, UIComponent toValidate, Object value) {
                        Integer featureValue = (Integer) value;
                
                        Conversation conversation = Conversation.instance();
                        Boolean longRunning = conversation.isLongRunning();
                        String id =conversation.getId();
                        
                        // Only validate the feature number if it is different from the current feature.
                        if (this.feature != null) {
                            if (featureValue.compareTo(feature.getNumber()) == 0) {
                                return;
                            }
                        }
                
                        // Determine if feature number is valid
                        if (featureValue.equals(12345)) {
                            this.feature = new Feature();
                            this.feature.setNumber(12345);
                            this.feature.setName("Test feature name.");
                            Solution solution = new Solution();
                            solution.setName("Solution name");
                            this.feature.setSolution(solution);
                            Status status = new Status();
                            status.setDisplay("Open");
                            this.feature.setStatus(status);
                            User owner = new User();
                            owner.setOperatorId("owner");
                            this.feature.setOwner(owner);
                            Pipeline pipeline = new Pipeline();
                            pipeline.setName("Solution_Test");
                            this.feature.setPipeline(pipeline);
                        } else {
                            this.feature = null;
                            ((UIInput) toValidate).setValid(false);
                            String message = BundleUtil.getResourceBundleString("featureError", featureValue);
                            context.addMessage(toValidate.getClientId(context), new FacesMessage(message));
                            return;
                        }
                    }
                }
                "
                
                



                • 5. Re: Long Running Conversation Question
                  Brian Wallace Newbie

                  I think I may have found my problem.  I can't say that I understand why it caused the problem, but removing the following configuration from my web.xml file fixed my problems with conversations:



                      <context-param>
                          <param-name>facelets.BUILD_BEFORE_RESTORE</param-name>
                          <param-value>true</param-value>
                      </context-param>



                  I believe I had read somewhere that someone had added this configuration to the web.xml file to resolve an issue they had with logging out of their JSF application.  I had used it but never removed it after incorporating Seam in the project.