10 Replies Latest reply on Nov 22, 2009 12:07 AM by Dmitry Shultz

    Factory method - how many times it should be called?

    Dmitry Shultz Newbie
      Hi,

      Have spent the whole day in debugger and still can't figure out why my @Factory method called twice during rendering of the same page.

      The request is started by s:link :

      `
      <s:link view="someViewId" propagation="begin">
        <f:param name="param1" value="val1"/>
      `

      The 'someViewId' doesn't have any page action or navigation rule
      Action component with the Factory method is defined as follows:


      `

      `
      The data is just some entity loaded from db.


      I can see there are two org.jboss.seam.contexts.ServerConversationContext created during the request processing, so  the Factory method is invoked for each of them (and 'data' is looked up and loaded for each of them too).

      Stack for both times the context is created is identical (the phase is RESTORE_VIEW):



      `@Name("taskAction")     
      @Scope(ScopeType.CONVERSATION)
      public class SomeViewAction {

         @Out(required = false)
         SomeEntity data;


         @Factory("data")
         public void loadData(){
         
            //load data from database
            ....
         }
        
      }`


      The stack for both contexts creation is identical (both at the same RESTORE_VIEW phase)

      at org.jboss.seam.contexts.ServerConversationContext.<init>(ServerConversationContext.java:72)
                at org.jboss.seam.contexts.FacesLifecycle.resumeConversation(FacesLifecycle.java:169)
                at org.jboss.seam.jsf.SeamPhaseListener.afterRestoreView(SeamPhaseListener.java:393)
                at org.jboss.seam.jsf.SeamPhaseListener.afterServletPhase(SeamPhaseListener.java:230)
                at org.jboss.seam.jsf.SeamPhaseListener.afterPhase(SeamPhaseListener.java:196)
                at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:280)
                at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:117)
                at javax.faces.webapp.FacesServlet.service(FacesServlet.java:244)
                at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:411)
                at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:317)
                at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:198)
                at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java)



      The second context is created at the very end of request processing (when the page is already rendered in the browser, so question  is what happened with the first conversation context? Why it became obsolete or forgotten?


      Thanks
        • 1. Re: Factory method - how many times it should be called?
          Dmitry Shultz Newbie

          This is really gets me - I still have two Conversation contexts created during the same page rendering.

          Both contexts are alive and it doesn't matter what i do the @Factory or @Create methods of involved on the page components are called twice (for each context) loading stuff from DB twice as well.


          I can't find any explanation of it in any book or online, but it is pretty consistent - first the page is fully rendered using components created by one conversation context and when it is almost finished the second conversation is kicked in (while the first is still alive!)...


          Why Seam is creating the second one? Is there any listener or interceptor can help in debugging it?


          I'm running Seam on Glassfish (9.1)


          Any help will be very appreciated.

          • 2. Re: Factory method - how many times it should be called?
            Arbi Sookazian Master

            Do you have a @Begin anywhere in your class?  The @Factory method is called only when the context variable it is associated with (in your case data) is null.


            If you are not promoting the temp conversation to LRC via @Begin or another way (pages.xml or s:link or s:button), then it may get called more than once per use case (conversation)...

            • 3. Re: Factory method - how many times it should be called?
              Dmitry Shultz Newbie

              Arbi,


              Here are some more details:


              because I want the fresh Conversation to be started and old one ended each time user is redirected to its home page there is endConverstaion specified in pages.xml:



              <page view-id="*">
                      <navigation>
                          <rule if-outcome="applicantHome">
                              <end-conversation/>
                              <redirect view-id="/secure/applicantHome.xhtml"/>
                          </rule>
                      </navigation>
               </page>




              For the /secure/applicantHome.xhtml new conversation is started by page action:



              <page>  
                <begin-conversation join="true"/>
              </page>




              But when 'applicantHome' outcome is received there are two conversations created (initiating two ApplicantHome component @Create invocations).



              This is probably because of infamous problem of 'one click conversation stop and start another', - i need to finish the old conversation and start a new one, but it is separated to different places (files).


              What I'm missing here?







               




              • 4. Re: Factory method - how many times it should be called?
                Shervin Asgari Master

                I think the problem is simply that you have one @Out with the name data, and then you have @Factory with the same name.
                Give your factory another name. If you want to outject then let the factory outject and drop the @Out.


                Read this very nice article to understand the difference between factory, unwrap and out here

                • 5. Re: Factory method - how many times it should be called?
                  Dmitry Shultz Newbie

                  Shervin,


                  The code with @Factory was refactored 10 times already since I've posted the initial post :) i.e. there is no @Factory anymore, but the problem repeats itself with @Create method - it is called twice for the flow described above.


                  Any other ideas why it can be?

                  • 6. Re: Factory method - how many times it should be called?
                    Arbi Sookazian Master

                    @Factory does not outject.  Usage of @Factory and @DataModel is common in Seam components, however, but @DataModel outjects (a ListDataModel typically).



                    Marks a method as a factory method for a context variable. A factory method is called whenever no value is bound to the named context variable, and is expected to initialize the value of the context variable.

                    http://docs.jboss.org/seam/2.2.0.GA/api/org/jboss/seam/annotations/Factory.html


                    There is nothing wrong with using @Factory and @Out AFAIK...

                    • 7. Re: Factory method - how many times it should be called?
                      Shervin Asgari Master

                      Arbi Sookazian wrote on Nov 21, 2009 03:36:


                      @Factory does not outject.  Usage of @Factory and @DataModel is common in Seam components, however, but @DataModel outjects (a ListDataModel typically).

                      There is nothing wrong with using @Factory and @Out AFAIK...


                      That can't be right. I am outjecting Factories all over the place. Take a look here for instance:




                      @Factory("organizations")
                          public List<Organization> getAllOrganizations() {
                           return entityManager.createQuery("SELECT distinct org FROM " + Organization.class.getName() + " org " 
                           + "join org.applicationprocesses order by org.name").getResultList();
                      }



                      Here I am outjecting the list to the name organizations using the @Factory annotation.
                      And in the xhtml I have:




                      <s:selectItems value="#{organizations}" />



                      and that works fine, so that means the @Factory must be outjecting.
                      Also I didn't say that its a problem using @Factory and @Out together, I do it too. But I do it slightly different.


                      I have a @Create which populates a list. That list is annotated with @Factory, which injects the list which is annotated with @Out.




                      @Out
                      List<SystemLog> allSystemLog;
                      
                      @Create
                       public void setup() {
                         retrieveSystemLog();
                       }
                      
                      @Factory
                      public void retrieveSystemLog() {
                         this.allSystemLog = //getting from database
                      }
                      


                      This works pretty good.



                      • 8. Re: Factory method - how many times it should be called?
                        Shervin Asgari Master

                        Dmitry Shultz wrote on Nov 21, 2009 02:32:


                        Shervin,

                        The code with @Factory was refactored 10 times already since I've posted the initial post :) i.e. there is no @Factory anymore, but the problem repeats itself with @Create method - it is called twice for the flow described above.

                        Any other ideas why it can be?


                        Hi Dmitry.
                        If the @Create method is executed twice, then obviously you are creating two conversations. Try debugging to see what the conversation id is.
                        Conversation.instance().getId()


                        Also I believe its a bad idea to end and create a conversation in the same place. If you really need a conversation at the applicantHome, then create a new one. Let the other timeout. Also create a conversation (temporary) and only propagate to long-running (Begin) when the user clicks a link or something that requires the same conversation. Also on the link of applicantHome (if you are not sure if a long running conversation is active), you should put propagation="none"

                        • 9. Re: Factory method - how many times it should be called?
                          Dmitry Shultz Newbie

                          Shervin,


                          I can see both conversations and their id's in the debug page, they are both long running and not nested.


                          In my case ending one and creating another conversation would be handy, I can't see why it is a bad idea.


                          New conversation is created because applicantHome.page.xml has the begin-conversation tag...


                          What I don't understand is why Seam creates the second conversation while the first one is still a live.

                          • 10. Re: Factory method - how many times it should be called?
                            Dmitry Shultz Newbie

                            Even if I change the applicantHome component scope to PAGE and remove all conversation management related tags the @Create method still called twice, so one PAGE context is replaced by another one during the same page rendering.


                            What makes Seam to forget about one page/conversation context and create another?