9 Replies Latest reply on Jun 26, 2008 5:35 PM by Ian McMahon

    Don't fully understand @Factory

    Ian McMahon Newbie

      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. There are two kinds of factory methods. Factory methods with void return type are responsible for outjecting a value to the context variable. Factory methods which return a value do not need to explicitly ouject the value, since Seam will bind the returned value to the specified scope. This annotation supports use of the Seam 'factory component' pattern.

      The messages example uses the void return type version, as such:



         @DataModel 
         private List<Message> messageList;
         
         @DataModelSelection
         @Out(required=false)
         private Message message;
         
         @PersistenceContext(type=EXTENDED)
         private EntityManager em;
         
         @Factory("messageList")
         public void findMessages()
         {
            messageList = em.createQuery("from Message msg order by msg.datetime desc")
                            .getResultList();
         }




      Presumably, whenever #{messageList} is encountered in the view, Seam will invoke findMessages() if necessary and then outject the value of messageList in the context so the EL expression can render it.


      In my example, I tried it both ways.  Done as above, my code looks like this:
        


          @DataModel
          private List<String> things;
      
          @Factory("things")
          public void findThings() {
              things = new ArrayList<String>();
              things.add("One thing");
              things.add("Two thing");
              things.add("Red thing");
              things.add("Blue thing");
          }




      That's in a SFSB.   I traced through all the code, and when #{things} is evaluated, it figures out that the factory method needs to be run, the factory method runs which populates the things member of my SFSB, but then it either outjects the return value of the factory method, which is null, or just runs the factory method and gives up!  I'm not 100% sure what's going on there, but it doesn't seem correct either way.


      On the other hand, if I let the Factory method return the List value, as such:



          @Factory("things")
          public List<String> findThings() {
              List<String> things = new ArrayList<String>();
              things.add("One thing");
              things.add("Two thing");
              things.add("Red thing");
              things.add("Blue thing");
              return things;
          }




      then it works correctly and my view is populated!   All the examples do it the other way, which is why I'm concerned, and I'm not sure if the benefits that @DataModel gives you are available if I do it this way...

        • 1. Re: Don't fully understand @Factory
          Guillaume Jeudy Master

          Have you tried replacing @DataModel with @Out in your example and do the same tracing you did ? I've used the @Factory with void return value and @Out successfully in my application.


          Maybe it's just the combination of @Factory and @DataModel that doesn't quite work.

          • 2. Re: Don't fully understand @Factory
            nimo stephan Master

            The @DataModel annotation implies @Out (automatically).


            If you do it with the factory-void-combination, do not forget the getter (setter) for your 'messageList'.



            • 3. Re: Don't fully understand @Factory
              Ian McMahon Newbie

              do you need a getter for it?   All the examples I've seen don't actually have a getter.  I thought that was the point of the 'outjection'; the @Out annotation tells seam that this is a component and it gets placed in the appropriate context.



              @Out instead of @DataModel doesn't change anything.

              • 4. Re: Don't fully understand @Factory
                nimo stephan Master

                yes u are right..but with your void..try it with a getter.

                • 5. Re: Don't fully understand @Factory
                  Guillaume Jeudy Master

                  You shouldnt need a getter/setter, @Out will do the job for you. By the way you don't need the variable to be a seam component to be able to outject it.


                  Have you tried in your example to use @Out instead of @DataModel ? I confirm 100% this usecase works, if it doesn't work on your end there must be something else that we missed and cause it to fail.

                  • 6. Re: Don't fully understand @Factory
                    Ian McMahon Newbie

                    yeah, I've tried using @Out in place of @DataModel, and I get the same behavior.  The Factory method runs but the variable is null on the web tier end of the invocation.


                    For what it's worth, adding a getter doesn't help at all.  I set a breakpoint in the getter and the factory method; the factory method is called, but the getter is not.

                    • 7. Re: Don't fully understand @Factory
                      Jacob Orshalick Apprentice

                      This should certainly work.  We are going to need some more detail to help (e.g. the full component definition, the facelet snippet where it is being requested, etc).  Also, what context is the @DataModel being outjected to?  You can view the details of the context in the Seam debug page.

                      • 8. Re: Don't fully understand @Factory
                        Ian McMahon Newbie

                        Hm, I didn't even know about the debug page, that's a handy bit of info to have! 


                        Anyway, aircraftManager component has two variables in it that the view ought to be able to see:


                            @DataModel
                            private List<String> things;
                        
                            @Factory("things")
                            public void findThings() {
                                things = new ArrayList<String>();
                                things.add("One thing");
                                things.add("Two thing");
                                things.add("Red thing");
                                things.add("Blue thing");
                            }
                        
                            @Factory("categories")
                            public List<AircraftCategory> getCategories() {
                                List<AircraftCategory> c = em.createQuery("SELECT c FROM AircraftCategory c").getResultList();
                                return c;
                            }



                        After I hit my test page, aircraftManager is in the SESSION context.  categories shows up under it on the debug page, but things doesn't.



                        Here's the facelet that requests things:


                                <ui:define name="content">
                                    <h1>Test</h1>
                        
                                    <f:view>
                                        <h:form>
                                            <h:dataTable var="thing" value="#{things}">
                                                <h:column>
                                                    <f:facet name="header">Things</f:facet>
                                                    <h:outputText value="#{thing}"/>
                                                </h:column>
                                            </h:dataTable>
                                        </h:form>
                                    </f:view>
                        
                                </ui:define>




                        • 9. Re: Don't fully understand @Factory
                          Ian McMahon Newbie

                          Here's the whole component if it helps:


                          @Stateful
                          @Name("aircraftManager")
                          @Scope(ScopeType.SESSION)
                          public class AircraftManagerBean implements Serializable, AircraftManagerLocal {
                              private static final long serialVersionUID = 1881413500711441951L;
                              
                              @PersistenceContext(unitName="av8", type= PersistenceContextType.EXTENDED)
                              private EntityManager em;
                          
                              @DataModel
                              private List<String> things;
                          
                              @Factory("things")
                              public void findThings() {
                                  things = new ArrayList<String>();
                                  things.add("One thing");
                                  things.add("Two thing");
                                  things.add("Red thing");
                                  things.add("Blue thing");
                              }
                          
                              @Factory("categories")
                              public List<AircraftCategory> getCategories() {
                                  List<AircraftCategory> c = em.createQuery("SELECT c FROM AircraftCategory c").getResultList();
                                  return c;
                              }
                          
                              public String loadBaseInfo() {
                                  AircraftCategory cat = new AircraftCategory();
                                  cat.setName("Airplane");
                                  em.persist(cat);
                          
                                  cat = new AircraftCategory("Rotorcraft");
                                  em.persist(cat);
                          
                                  return "aircraftManager.xhtml";
                              }
                          
                              @Remove public void destroy() {}
                          }