7 Replies Latest reply on Mar 12, 2010 5:11 PM by deadlockgr

    PAGE-scoped seam component constructor runs 4 times!

    deadlockgr

      Hi all!


      This is a newbie question. I a page using a PAGE-scoped component. I would expect that the constructor of the component to be executed once, upon creation of the page, then the component would be stored in the PAGE context, and everybody would be happy.


      But what happens is that my constructor runs 4 times and the @Create method runs 2 times. Why is this happening? If someone could take a look I would be grateful.


      Cheers!


      The output:


      Constructor
      Constructor
      @Create
      Constructor
      Constructor
      @Create



      The page:



      <!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:h="http://java.sun.com/jsf/html"
           xmlns:f="http://java.sun.com/jsf/core"
           xmlns:a4j="http://richfaces.org/a4j"
           xmlns:rich="http://richfaces.org/rich">
      
      <body>
      <ui:composition template="/WEB-INF/facelets/templates/template.xhtml">
      
           <ui:define name="content">
      
                <a4j:loadScript src="/js/focus.js" />
      
                <h:form prependId="false">
      
                     <!-- Hotkeys -->
                     <rich:hotKey key="ctrl+s"
                          handler="$('createButton').click();return false;" />
      
                     <h:commandButton action="#{applicantBean.cancelCreateApplicant}"
                          value="#{msgs.back_to_search_results}" immediate="true" />
                     <br />
                     <br />
      
                     <h:outputText value="#{msgs.create_applicant}" class="page_title" />
      
                     <table width="100%">
                          <tbody>
                               <tr>
                                    <td width="20%"><h:outputText value="#{msgs.name}" /></td>
                                    <td width="30%"><h:inputText id="applicantName"
                                         style="width:100%" value="#{applicantBean.applicant.name}"
                                         required="true" requiredMessage="#{val.a_value_is_required}" /></td>
                                    <td width="50%"><h:message for="applicantName"
                                         styleClass="errorMessage" /></td>
                               </tr>
                               <tr>
                                    <td><h:outputText value="#{msgs.email}" /><h:outputText
                                         value=" #{msgs.optional_in_parenthesis}" styleClass="optional" /></td>
                                    <td><h:inputText id="applicantEmail" style="width:100%"
                                         value="#{applicantBean.applicant.receiptEmail}">
                                         <f:validator validatorId="EmailValidator" />
                                    </h:inputText></td>
                                    <td><h:message for="applicantEmail" styleClass="errorMessage" /></td>
                               </tr>
                               <tr>
      
                                    <td colspan="2" align="right" valign="middle">
      
                                    <p><h:commandButton id="createButton"
                                         action="#{applicantBean.createApplicant}" value="#{msgs.create}">
                                    </h:commandButton> <h:commandButton action="#{applicantBean.cancelCreateApplicant}"
                                         value="#{msgs.cancel}" immediate="true">
                                    </h:commandButton></p>
                                    </td>
      
                               </tr>
                               <tr>
                                    <td><h:messages errorClass="errorMessage"
                                         infoClass="errorMessage" layout="table" globalOnly="true"
                                         showDetail="false" showSummary="true" /></td>
                               </tr>
                          </tbody>
                     </table>
      
      
      
                </h:form>
      
                <script type="text/javascript">
                     setFocus("#{(focus != null) ? focus : 'applicantName'}");
                     setHighlight('${highlight}');
                </script>
      
           </ui:define>
      </ui:composition>
      </body>
      </html>



      and the component:




      
      import org.jboss.seam.ScopeType;
      import org.jboss.seam.annotations.AutoCreate;
      import org.jboss.seam.annotations.Create;
      import org.jboss.seam.annotations.Name;
      import org.jboss.seam.annotations.Scope;
      
      /**
       * Managed bean to handle product instances.
       * 
       * 
       */
      @Name("productBean")
      @Scope(ScopeType.PAGE)
      public class ProductBean extends BasePageManagedBean {
      
           /**
            * The logger.
            */
           private final static Logger logger = Logger.getLogger(ProductBean.class);
           /**
            * Public field for ad-hoc injection to work.
            */
           @EJB(name = "ProductFacadeService")
           public ProductFacadeService productFacadeService;
           private Product product;
      
           public ProductBean() {
                System.out.println("Constructor");
           }
           
           @Create
           public void onCreate() {
                product = new Product();
                System.out.println("@Create");
           }
      
           /**
            * This method creates a product
            * 
            * @return the action to be taken
            */
           public Navigation.EditProduct createProduct() {
      
                if (!productFacadeService.canCreate(product)) {
                     setMessage("val", "product_name_already_exists", null,
                               FacesMessage.SEVERITY_ERROR, "productName");
                     return null;
                }
                productFacadeService.create(product);
                if (logger.isTraceEnabled()) {
                     logger.trace("Created product: " + product);
                }
                return Navigation.EditProduct.SHOW_APPLICANT_PRODUCT_LIST;
           }
      
      
           /**
            * Getter for the product
            * 
            * @return the product
            */
           public Product getProduct() {
      
                return product;
           }
      
           /**
            * Setter for the product
            * 
            * @param product
            *            the product to set
            */
           public void setProduct(Product product) {
      
                this.product = product;
           }
      
           /**
            * Setter for the applicant of the product
            * 
            * @param applicant
            *            the applicant to be set
            */
           public void setApplicant(Applicant applicant) {
      
                getProduct().setApplicant(applicant);
           }
      
           /**
            * Navigation-returning method, returns the action to follow after canceling
            * creating a product.
            * 
            * @return the action to be taken
            */
           public Navigation.CreateProduct cancelCreateProduct() {
      
                return (Navigation.CreateProduct.SHOW_APPLICANT_PRODUCT_LIST);
           }
      
      }
      






        • 1. Re: PAGE-scoped seam component constructor runs 4 times!
          ssamayoagt

          I dont see productBean referenced in the view.


          How your component is instantiated?


          Regards.

          • 2. Re: PAGE-scoped seam component constructor runs 4 times!
            deadlockgr

            Sorry, you are right. I pasted a similar page. This is the correct one, which uses the productBean component.


            <!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:h="http://java.sun.com/jsf/html"
                 xmlns:f="http://java.sun.com/jsf/core"
                 xmlns:a4j="http://richfaces.org/a4j"
                 xmlns:rich="http://richfaces.org/rich">
            
            <body>
            <ui:composition template="/WEB-INF/facelets/templates/template.xhtml">
            
                 <ui:define name="content">
            
                      <a4j:loadScript src="/js/focus.js" />
            
                      <!-- Hotkeys -->
                      <rich:hotKey key="ctrl+s"
                           handler="$('createButton').click();return false;" />
            
                      <h:form prependId="false">
            
                           <a4j:keepAlive beanName="productBean" />
            
                           <h:commandButton action="#{productBean.cancelCreateProduct}"
                                value="#{msgs.back_to_search_results}" immediate="true" />
                           <br />
                           <br />
            
                           <h:outputText value="#{msgs.create_product}" class="page_title" />
            
                           <table width="100%">
                                <tbody>
                                     <tr>
                                          <td width="20%"><h:outputText value="#{msgs.name}" /></td>
                                          <td width="30%"><h:inputText id="productName"
                                               style="width:100%" value="#{productBean.product.inventedName}"
                                               required="true" requiredMessage="#{val.a_value_is_required}" /></td>
                                          <td width="50%"><h:message styleClass="errorMessage"
                                               for="productName" /></td>
                                     </tr>
                                     <tr>
                                          <td><h:outputText value="#{msgs.email}" /><h:outputText
                                               value=" #{msgs.optional_in_parenthesis}" styleClass="optional" /></td>
                                          <td><h:inputText id="productEmail" style="width:100%"
                                               value="#{productBean.product.receiptEmail}">
                                               <f:validator validatorId="EmailValidator" />
                                          </h:inputText></td>
                                          <td><h:message styleClass="errorMessage" for="productEmail" /></td>
                                     </tr>
                                     <tr>
                                          <td><h:outputText value="#{msgs.applicant}" /></td>
                                          <td><h:inputText
                                               style="width:100%" value="#{productBean.product.applicant.name}" disabled="true" />
                                          </td>
                                     </tr>
            
                                     <tr>
                                          <td colspan="2" align="right" valign="middle">
            
                                          <p><h:commandButton id="createButton" action="#{productBean.createProduct}"
                                               value="#{msgs.create}" /> <h:commandButton
                                               action="#{productBean.cancelCreateProduct}"
                                               value="#{msgs.cancel}" immediate="true" /></p>
                                          </td>
            
                                     </tr>
                                </tbody>
                           </table>
            
                      </h:form>
            
                      <script type="text/javascript">
                           setFocus("#{(focus != null) ? focus : 'productName'}");
                           setHighlight('${highlight}');
                      </script>
            
                 </ui:define>
            </ui:composition>
            </body>
            </html>



            • 3. Re: PAGE-scoped seam component constructor runs 4 times!
              ssamayoagt
              I think your problem is this:

              <a4j:keepAlive beanName="productBean" />

              There is two instances of the same bean, one in the page tree and other in wherever Rich Faces stores the bean for keep alive implementation.

              Regards.
              • 4. Re: PAGE-scoped seam component constructor runs 4 times!
                deadlockgr

                Sergio, thanks for the reply.


                Indeed, a4j:keepalive was responsible for half the initialization. Now that I have removed that line, I get this:


                Constructor
                Constructor
                @Create


                Why does the constructor run twice? Is it perhaps related to the JSF lifecycle underneath Seam, and it is normal?


                If it is, it is very restricting of someone wants to do some datamodel initialization in the constructor...


                Cheers!

                • 5. Re: PAGE-scoped seam component constructor runs 4 times!
                  niox.nikospara.yahoo.com

                  The constructor may run twice as a result of some proxy creation. I think Seam does this for conversation-scoped components, I am not sure about page-scoped. Placing a breakpoint in the constructor and observing both the stack (who called it?) and the exact type of this object (is this a real object or a proxy?) could provide some insight...

                  • 6. Re: PAGE-scoped seam component constructor runs 4 times!
                    niox.nikospara.yahoo.com

                    By the way I believe that the @Create methods are the prefered way of initializing a component.

                    • 7. Re: PAGE-scoped seam component constructor runs 4 times!
                      deadlockgr

                      Kalispera Niko!


                      Well, the way I see it, that could very well be the case.


                      I did run the debugger, and the results are enlightening.


                      First of all, the first invocation is on the component itself (ProductBean), the second one is on its proxy (ProductBean$$javassistseam5@18669ac), so you are right.


                      Both request come from the HtmlInputText (the first of the two, as both beans will be available for the remaining JSF components).


                      Cheers,


                      Markos