9 Replies Latest reply on Feb 16, 2009 4:37 PM by Hal Johnson

    Page Parameters, @Factory and persisting

    Hal Johnson Newbie
      I'm still climbing the learning curve of seam, so I apologize in advance if the answer to this is trivially obvious.

      I have a simple Entity bean with an id and a character field.  I have a page parameter that associates the id with a property of a helper seam component (pojo).  This component has a @Factory annotated method that creates the Entity bean by looking up the id (from the page parameter).  A single <h:inputText> on the page is associated with the character field in the Entity bean.

      Up until this point, everything works fine.  Request the page, the proper record is queried and everything is populated.

      The problem occurs when I try to create a bit of simple functionality to allow the character field to be edited and saved.

      I have a simple method in the helper class that essentially just merges the Entity bean's changes.

      If I use a <s:button>, the Entity bean seems to be reloaded by the @Factory method and any changes are lost.  I have tried setting the scope parameter of the @Factory annotation to CONVERSATION, but it doesn't seem to make a difference.  From the documentation I gathered that factory methods only get called when the component is null, so I'm not quite sure whats happening here.

      If I use a <h:commandButton>, the page parameter doesn't seem to populate, which causes my factory method to throw an exception (intentionally, for debugging purposes as I try to understand the problem).  This also leads me to believe that the component isn't being propagated somehow.

      This seems like awfully basic functionality to me, which makes me think I'm missing something very fundamental.

      What is the standard pattern for handling simple update pages with page parameters?  Should I not be using a @Factory?  Is there a reason the component the factory is generating isn't being propagated, even when it's scope is set to CONVERSATION?

      Thank you in advance for any help that you're able to provide!

      -Hal
        • 2. Re: Page Parameters, @Factory and persisting
          Hal Johnson Newbie

          I was rather hoping to avoid that, since this is more of a general design question (I don't want to get into bad habits from the beginning).  But I appreciate your time, and I will type out as simple of an example as I can here to illustrate my problem.


          Say I have a simple Entity bean:


          @Entity
          public class Info {
            private Integer id;
            private String text;
          
            @Id
            public Integer getId() {
              return id;
            }
          
            public void setId(Integer id) {
              this.id = id;
            }
          
            public String getText() {
              return text;
            }
          
            public void setText(String text) {
              this.text = text;
            }
          }



          I also have a simple helper pojo, providing a store for a page parameter as well as a factory for the Info entity:


          @Name("infoHelper")
          public class InfoHelper {
            private Integer id;
            @In EntityManager em;
          
          
            public Integer getId() {
              return id;
            }
          
            public void setId(Integer id) {
              this.id = id;
            }
            
            @Factory(value="info", scope=ScopeType.CONVERSATION)
            public Info getInfo() {
              return em.find(Info.class, getId());
            }
          
            public String save() {
              em.flush();
            }
          }



          And an excerpt from pages.xml:


          <page view-id="/whatever.xhtml">
            <param name="id" value="#{infoHelper.id}"/>
          </page>



          And in whatever.xhtml, I'd have something like:


          <h:form>
            <h:inputText value="#{info.text}"/>
            <h:commandButton value="Save" action="#{infoHelper.save}">
              <f:param name="id" value="#{infoHelper.id}"/>
              <s:conversationPropagation/>
            </h:commandButton>
          </h:form>



          The problem now is the info component is disappearing by time InfoHelper.save gets called.  The id page parameter is not propagating, so the factory method is setting info to null.


          Shouldn't info remain in the conversation context until the post request from the commandButton?  If it is in the conversation context, why is the getInfo() method being called?  From the documentation I gathered that the factory method is only called when the component doesn't exist.


          Thanks in advance for any help!


          -Hal

          • 3. Re: Page Parameters, @Factory and persisting
            Hal Johnson Newbie

            PS - The InfoHelper.save() method does return a value, I've tried returning null, /default.xhtml, etc to no avail.  I typed this out from memory so I apologize for any other simple typos.

            • 4. Re: Page Parameters, @Factory and persisting
              Binesh Gummadi Novice

              What you read about factory annotation is correct. Factory method is only called if the value is null.


              In your factory method, getId() always returns null and hence entityManager cannot find Info object.


              @Factory(value="info", scope=ScopeType.CONVERSATION)
                public Info getInfo() {
                  return em.find(Info.class, getId());
                }
              



              You can verify if info object is available in debug page. Enable seam debugging in components.xml with


              <core:init debug="true" />



              • 5. Re: Page Parameters, @Factory and persisting
                Hal Johnson Newbie

                Thank you for your response Binesh, but I think you may have misunderstood.  I'll try to make my problem a bit clearer.


                Say I request a page, such as:



                /whatever.seam?id=1



                This is fine, the page will load, it will find the proper record, and the factory method will create the correct Info object.


                The problem occurs when I press the form button to save the changes to the text field.  By time the save method is called, the info component is gone and I'm not sure why.  It should be in the conversation context, and from my understanding, the conversation context should exist until the save method is called (when the POST request is being handled).


                The getInfo() method will return null at this point, since the page parameter isn't propagating (this is the second part of my problem, but I'd like to work out one bit at a time).  But the info component should still exist, so the factory method shouldn't even be called.


                What am I doing wrong?  Why isn't the info component still in the conversation context for my save call?


                I know I'm missing something fairly fundamental, so please bear with me, and thank you for any help!


                -Hal

                • 6. Re: Page Parameters, @Factory and persisting
                  Marcio Endo Newbie

                  I have a simple Entity bean with an id and a character field.  I have a page parameter that associates the id with a property of a helper seam component (pojo).  This component has a @Factory annotated method that creates the Entity bean by looking up the id (from the page parameter).  A single <h:inputText> on the page is associated with the character field in the Entity bean.


                  This sounds like exactly what an EntityHome does.


                  I'd suggest you create a simple project with seam-gen and look at the generated Java code and .xhtml pages. It will give you hints on how this could be accomplished.


                  Apart from that, IIRC, <h:commandButton> does not take <f:param> as a child element. That is one of the reasons why <s:button> was created. Also, it sounds like you are not explicitly starting any conversation; without that, any modifications you make will be lost.

                  • 7. Re: Page Parameters, @Factory and persisting
                    Binesh Gummadi Novice

                    s:button will not submit the form. You can use a4j:commandButton and a4j:actionParam.
                    Even though starting a conversation will solve many problems this is not the only way to modify data.


                    If you are looking for a standard to do CRUD operations in your application I agree with Marcio that you should use seam-gen to create your project and see how everything is wired together.


                    Your InfoHelper is not conversation scoped and when you click on save button it doesn't not participate in the same conversation. How it works is, seam starts a temporary conversation every request and if your bean is conversation scoped then your long running conversation and the temporary conversation is merged and you will have access to your values.


                    Does it make sense?


                    • 8. Re: Page Parameters, @Factory and persisting
                      Hal Johnson Newbie

                      Thank you for taking the time to reply Mario.  In an effort to further my understanding of what should be exceedingly basic behavior, I recreated my issue with as minimal of an implementation as possible.  Not too surprisingly it works fine as is, which means I have something conflicting with my original implementation.  This is entirely my fault and I will have to spend some time dwelling on what is causing the problem.


                      I had a feeling <h:commandButton>'s didn't use <f:param> child elements, but I was at the stage of trying any and everything to solve the problem.


                      I had come across the EntityHome class during my learning endeavors, but haven't investigated it yet. I will do so at my first opportunity since you mentioned it may fit the functional pattern I am looking for.  Perhaps it will provide a more elegant approach.


                      Thank you again, take care!


                      -Hal


                      PS - After originally reading your response, I tried starting a long running conversation with @Begin on the InfoHelper.save() method, but it made no difference.  Turns out the temporary conversation is all that seems to be necessary (thought I remembered reading in the docs that it persists across JSF callbacks, and it does in fact seem to).

                      • 9. Re: Page Parameters, @Factory and persisting
                        Hal Johnson Newbie

                        Thank you as well Binesh, did not notice your reply until after I made my last post.


                        I never expected the InfoHelper bean to persist, although it looks at first glance like it stores state (getId/setId), that property just exists to hold the page parameter during the request.  It was the info component the factory created that I was trying to use as a backing bean for the form.


                        As I mentioned in my previous response, I created as minimal of an implementation as possible and it works as I figured it should.  This means I have something somewhere conflicting with the original code.


                        I appreciate your time and help, and apologize for what was apparently my own mistake.  Now I just need to locate it. :)


                        Best wishes.


                        -Hal