10 Replies Latest reply on Dec 21, 2011 5:34 AM by jackhopes

    Multiple DataModels scoped to PAGE context, only 1 Factory called when returning to page

      I have two h:dataTable components on the same page (home.xhtml).  The associated lists (list1 & list2) are marked with @DataModel(scope=ScopeType.PAGE) in PageTestBean.  Each list has its own @Factory.  Objects are displayed in the first or second table depending if the number in member variable tableNum is 1 or 2.


      Clicking on an item in one of the tables takes navigates to toggle.xhtml, where I toggle the tableNum value between 1 and 2. Toggling takes us back to home.xhtml.  When home.xhtml is loaded this second time only the factory for the first dataTable is called.  So what ends up happening is that the items list in the first dataTable are updated while those in the second are not.


      I've played around with this a fair bit and I'm thinking this might be a bug.  My understanding is that the factories of both lists should be getting called when the page is reloaded.  But I'm new to Seam so maybe someone else can see my problem.


      
      //Table1.java
      
      package com.cs.PageTest.entity;
      
      
      ...
      
      
      @Entity
      
      @Table(name = "table1")
      
      public class Table1 implements java.io.Serializable {
      
      
           private int id;
      
           private int tableNum;
      
           
      
           public Table1() {
      
           }
      
      
           @Id
      
           @GeneratedValue
      
           @Column(name = "id", unique = true, nullable = false)
      
           @NotNull
      
           public int getId() {
      
                return this.id;
      
           }
      
      
           public void setId(int id) {
      
                this.id = id;
      
           }
      
      
           @Column(name = "table_num", unique = false, nullable = false)
      
           @NotNull
      
           public int getTableNum() {
      
                return this.tableNum;
      
           }
      
           
      
           public void setTableNum(int tableNum) {
      
                this.tableNum = tableNum;
      
           }
      
      }
      
      



      
      //PageTestBean.java
      
      package com.cs.PageTest;
      
      
      ...
      
      
      @Stateful
      
      @Name("pageTest")
      
      public class PageTestBean implements PageTest {
      
           
      
           @In(create=true)
      
           private EntityManager entityManager;
      
           
      
           @Out(required=false)
      
           Table1 table1;
      
      
           @DataModel(scope=ScopeType.PAGE)
      
           private List<Table1> list1;
      
           
      
           @DataModel(scope=ScopeType.PAGE)
      
           private List<Table1> list2;
      
           
      
           @Factory("list1")
      
           public void populateList1() {
      
                list1 = entityManager.createQuery(     "SELECT t " +
      
                                         "FROM Table1 AS t " +
      
                                         "WHERE t.tableNum = 1")
      
                                         .getResultList();
      
           }
      
           
      
           @Factory("list2")
      
           public void populateList2() {
      
                list2 = entityManager.createQuery(     "SELECT t " +
      
                                         "FROM Table1 AS t " +
      
                                         "WHERE t.tableNum = 2")
      
                                         .getResultList();
      
           }
      
           
      
           public String viewEntryButton(Table1 table1) {
      
                this.table1 = table1;
      
                return "togglePage";
      
           }
      
           
      
           @Remove
      
           public void destroy(){
      
                
      
           }
      
      }
      
      


      
      //PageTest.java
      
      package com.cs.PageTest;
      
      
      ...
      
      
      public interface PageTest {
      
           public String viewEntryButton(Table1 table1);
      
           
      
           public void populateList1();
      
           public void populateList2();
      
           
      
           public void destroy();
      
      }
      
      


      
      //Toggle.java
      
      package com.cs.PageTest;
      
      
      ...
      
      
      public interface Toggle {
      
           public String toggleButton();
      
           public Table1 getTable1();
      
           public void create();
      
           public void remove();
      
      }
      
      


      
      //ToggleBean.java
      
      package com.cs.PageTest;
      
      
      ...
      
      
      @Name("toggle")
      
      @Stateful
      
      public class ToggleBean implements Toggle{
      
           
      
           @In
      
           Table1 table1;
      
           
      
           @In
      
           EntityManager entityManager;
      
           
      
           @Create
      
           @Begin(join=true)
      
           public void create() {
      
                
      
           }
      
           
      
           public Table1 getTable1() {
      
                return this.table1;
      
           }
      
           
      
           public String toggleButton() {
      
                table1 = entityManager.merge(table1);
      
                table1.setTableNum(table1.getTableNum() == 1 ? 2 : 1);
      
                return "home";
      
           }
      
           
      
           @Remove
      
           public void remove() {
      
                
      
           }
      
      }
      
      


      
      <!--home.xhtml-->
      
      <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
      
                                   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      
      
      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
      
                      xmlns:s="http://jboss.com/products/seam/taglib"
      
                      xmlns:ui="http://java.sun.com/jsf/facelets"
      
                      xmlns:f="http://java.sun.com/jsf/core"
      
                      xmlns:h="http://java.sun.com/jsf/html">
      
                             
      
      <body>
      
      
           <h1>Test</h1>
      
      
           <div id="messages">
      
                <h:messages globalOnly="true" styleClass="message" id="globalMessages"/>
      
           </div>
      
      
                <h:form>                                   
      
                     <h:dataTable 
      
                          var="table11"
      
                          value="#{list1}"
      
                     >
      
                          <f:facet name="header">               
      
                               List 1
      
                          </f:facet>
      
                          <h:column>
      
                               <f:facet name="header">
      
                                    <h4></h4>
      
                               </f:facet>
      
                               <h:commandLink action="#{pageTest.viewEntryButton(table11)}">
      
                                    #{table11.id}
      
                               </h:commandLink>
      
                          </h:column>
      
                     </h:dataTable>
      
                </h:form>
      
                
      
                <h:form>                                   
      
                     <h:dataTable 
      
                          var="table12"
      
                          value="#{list2}"
      
                     >
      
                          <f:facet name="header">               
      
                               List 2
      
                          </f:facet>
      
                          <h:column>
      
                               <f:facet name="header">
      
                                    <h4></h4>
      
                               </f:facet>
      
                               <h:commandLink action="#{pageTest.viewEntryButton(table12)}">
      
                                    #{table12.id}
      
                               </h:commandLink>
      
                          </h:column>
      
                     </h:dataTable>
      
                </h:form>
      
                <h:form>
      
                     <s:link propagation="false" action="home.xhtml" value="refresh"/>
      
                </h:form>
      
      
      </body>
      
      
      </ui:composition>
      
      


      
      <!--toggle.xhtml-->
      
      <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
      
                                   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      
      
      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
      
                      xmlns:s="http://jboss.com/products/seam/taglib"
      
                      xmlns:ui="http://java.sun.com/jsf/facelets"
      
                      xmlns:f="http://java.sun.com/jsf/core"
      
                      xmlns:h="http://java.sun.com/jsf/html">
      
                             
      
      <body>
      
      
           <h1>Toggle</h1>
      
      
           <div id="messages">
      
                <h:messages globalOnly="true" styleClass="message" id="globalMessages"/>
      
           </div>
      
           
      
           <h:form>
      
                <h:panelGrid>
      
                     <h:outputText value="id: #{toggle.table1.id}"/>
      
                     <h:outputText value="table_num: #{toggle.table1.tableNum}"/>
      
                     <h:commandButton action="#{toggle.toggleButton}" value="Toggle"/>
      
                </h:panelGrid>
      
           </h:form>
      
      
      </body>
      
      
      </ui:composition>
      
      


      
      <!--pages.xml-->
      
      <?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="/home.xhtml"
      
             login-view-id="/login.xhtml">
      
      
          <page view-id="*">
      
              <navigation>
      
                  <rule if-outcome="home">
      
                      <redirect view-id="/home.xhtml"/>
      
                  </rule>
      
              </navigation>
      
          </page> 
      
          
      
          <page view-id="/home.xhtml">
      
               <navigation>
      
                    <rule if-outcome="togglePage">
      
                         <redirect view-id="/toggle.xhtml"/>
      
                    </rule>
      
               </navigation>
      
          </page>
      
      
          <exception class="org.jboss.seam.framework.EntityNotFoundException">
      
              <redirect view-id="/error.xhtml">
      
                  <message>Not found</message>
      
              </redirect>
      
          </exception>
      
          
      
          <exception class="javax.persistence.EntityNotFoundException">
      
              <redirect view-id="/error.xhtml">
      
                  <message>Not found</message>
      
              </redirect>
      
          </exception>
      
          
      
          <exception class="javax.persistence.OptimisticLockException">
      
              <end-conversation/>
      
              <redirect view-id="/error.xhtml">
      
                  <message>Another user changed the same data, please try again</message>
      
              </redirect>
      
          </exception>
      
          
      
          <exception class="org.jboss.seam.security.AuthorizationException">
      
              <redirect view-id="/error.xhtml">
      
                  <message>You don't have permission to do this</message>
      
              </redirect>
      
          </exception>
      
          
      
          <exception class="org.jboss.seam.security.NotLoggedInException">
      
              <redirect view-id="/login.xhtml">
      
                  <message>Please log in first</message>
      
              </redirect>
      
          </exception>
      
          
      
          <exception class="javax.faces.application.ViewExpiredException">
      
              <redirect view-id="/error.xhtml">
      
                  <message>Your session has timed out, please try again</message>
      
              </redirect>
      
          </exception>
      
           
      
          <exception>
      
              <redirect view-id="/error.xhtml">
      
                  <message>Unexpected error, please try again</message>
      
              </redirect>
      
          </exception>
      
          
      
      </pages>
      
      




        • 1. Re: Multiple DataModels scoped to PAGE context, only 1 Factory called when returning to page
          jimk1723

          I haven't looked at your code in detail, but is there a reason you're not using something like a PickList?

          • 2. Re: Multiple DataModels scoped to PAGE context, only 1 Factory called when returning to page
            jimk1723

            I'm not seeing this behavior. Are your snippets representative of your actual code?


            Here's the example I whipped up using a simple application-scoped list of POJOS in lieu of an entity. Toggle works, the lists update fine.


            ToggleBean.java


            package com.example;
            
            import javax.ejb.Remove;
            import javax.ejb.Stateful;
            
            import org.jboss.seam.annotations.In;
            import org.jboss.seam.annotations.Name;
            
            @Name("toggle")
            @Stateful
            public class ToggleBean implements Toggle {
                @In
                Table selectedTable;
            
                public String toggle() {
            
                    selectedTable.setOn(!selectedTable.isOn());
                    return "examplehome";
                }
            
                @Remove
                public void destroy() {
                }
            }
            
            



            Toggle.java


            package com.example;
            
            import javax.ejb.Remove;
            
            public interface Toggle {
            
                public abstract String toggle();
            
                @Remove
                public abstract void destroy();
            
            }
            



            TableFactory.java


            package com.example;
            
            import java.util.ArrayList;
            import java.util.List;
            
            import org.jboss.seam.ScopeType;
            import org.jboss.seam.annotations.Factory;
            import org.jboss.seam.annotations.Name;
            
            @Name("tableFactory")
            public class TableFactory {
                @Factory(value = "allTables", scope = ScopeType.APPLICATION)
                public List<Table> getTables() {
                    List<Table> list = new ArrayList<Table>();
                    list.add(new Table("a", true));
                    list.add(new Table("b", true));
                    list.add(new Table("c", false));
                    list.add(new Table("d", true));
                    list.add(new Table("e", false));
                    return list;
                }
            }
            



            TableExample.java


            package com.example;
            
            import java.util.ArrayList;
            import java.util.List;
            
            import javax.ejb.Remove;
            import javax.ejb.Stateful;
            
            import org.apache.commons.collections.CollectionUtils;
            import org.apache.commons.collections.Predicate;
            import org.jboss.seam.ScopeType;
            import org.jboss.seam.annotations.Factory;
            import org.jboss.seam.annotations.In;
            import org.jboss.seam.annotations.Name;
            import org.jboss.seam.annotations.Out;
            import org.jboss.seam.annotations.datamodel.DataModel;
            
            @Stateful
            @Name("pageTest")
            public class TableExampleBean implements TableExample {
                @DataModel(scope = ScopeType.PAGE)
                List<Table> list1;
                @DataModel(scope = ScopeType.PAGE)
                List<Table> list2;
            
                @In(create = true)
                List<Table> allTables;
            
                @Out(required = false, scope = ScopeType.SESSION)
                Table selectedTable;
            
                @Factory("list1")
                public void getList1() {
                    list1 = new ArrayList<Table>(CollectionUtils.select(allTables,
                            new Predicate() {
            
                                public boolean evaluate(Object arg0) {
                                    if (arg0 instanceof Table) {
                                        Table table = (Table) arg0;
                                        return table.isOn();
                                    }
            
                                    return false;
                                }
                            }));
                }
            
                @Factory("list2")
                public void getList2() {
                    list2 = new ArrayList<Table>(CollectionUtils.select(allTables,
                            new Predicate() {
            
                                public boolean evaluate(Object arg0) {
                                    if (arg0 instanceof Table) {
                                        Table table = (Table) arg0;
                                        return !table.isOn();
                                    }
            
                                    return false;
                                }
                            }));
                }
            
                public String selectTable(Table table) {
                    selectedTable = table;
                    return "togglePage";
                }
            
                @Remove
                public void destroy() {
                }
            }
            



            TableExample.java


            package com.example;
            
            import javax.ejb.Local;
            
            @Local
            public interface TableExample {
            
                public abstract void getList1();
            
                public abstract void getList2();
            
                public void destroy();
            
                public String selectTable(Table table);
            }
            



            Table.java


            package com.example;
            
            public class Table {
                boolean on;
                String name;
            
                public Table(String name, boolean on) {
                    super();
                    this.name = name;
                    this.on = on;
                }
            
                public boolean isOn() {
                    return on;
                }
            
                public void setOn(boolean on) {
                    this.on = on;
                }
            
                public String getName() {
                    return name;
                }
            
                public void setName(String name) {
                    this.name = name;
                }
            }
            
            



            lists.page.xml


            <!DOCTYPE page PUBLIC "-//JBoss/Seam Pages Configuration DTD 1.1//EN" "http://jboss.com/products/seam/pages-2.0.dtd">
            
            <page>
            
             <navigation>
              <rule if-outcome="togglePage">
               <redirect view-id="/example/toggle.xhtml" />
              </rule>
             </navigation>
            
            </page>
            



            lists.xhtml


            <!DOCTYPE composition 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" xml:lang="en" 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:s="http://jboss.com/products/seam/taglib"
             xmlns:a="https://ajax4jsf.dev.java.net/ajax" xmlns:rich="http://richfaces.org/rich">
             <body>
            
              <h1>Test</h1>
            
              <div id="messages">
               <h:messages globalOnly="true" styleClass="message" id="globalMessages" />
              </div>
            
              <h:form>
               <h:dataTable var="table11" value="#{list1}">
                <f:facet name="header">List 1</f:facet>
                <h:column>
                 <f:facet name="header">
                  <h4></h4>
                 </f:facet>
                 <h:commandLink action="#{pageTest.selectTable(table11)}">#{table11}</h:commandLink>
                </h:column>
               </h:dataTable>
              </h:form>
            
              <h:form>
               <h:dataTable var="table12" value="#{list2}">
                <f:facet name="header">List 2</f:facet>
                <h:column>
                 <f:facet name="header">
                  <h4></h4>
                 </f:facet>
                 <h:commandLink action="#{pageTest.selectTable(table12)}">#{table12}</h:commandLink>
                </h:column>
               </h:dataTable>
              </h:form>
              <h:form>
               <s:link propagation="false" action="home.xhtml" value="refresh" />
              </h:form>
            
             </body>
            </html>
            



            toggle.page.xml


            <!DOCTYPE page PUBLIC "-//JBoss/Seam Pages Configuration DTD 1.1//EN" "http://jboss.com/products/seam/pages-2.0.dtd">
            
            <page>
             <navigation>
              <rule if-outcome="examplehome">
               <redirect view-id="/example/lists.xhtml" />
              </rule>
             </navigation>
            </page>
            
            




            toggle.xhtml


            <!DOCTYPE composition 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" xml:lang="en" 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:s="http://jboss.com/products/seam/taglib"
             xmlns:a="https://ajax4jsf.dev.java.net/ajax" xmlns:rich="http://richfaces.org/rich">
             <body>
            
              <h1>Test</h1>
            
              <div id="messages">
               <h:messages globalOnly="true" styleClass="message" id="globalMessages" />
              </div>
            
            
            
              <h:form>
               #{selectedTable}
            
            
               #{selectedTable.on}
               <h:commandButton action="#{toggle.toggle}" />
            
            
              </h:form>
            
             </body>
            </html>
            


            • 3. Re: Multiple DataModels scoped to PAGE context, only 1 Factory called when returning to page

              I threw this example together in order to illustrate the problem I'm having with DataTables, so using a PickList wouldn't help with that :)


              Yes, these code snippets are actual code taken directly from my project.


              I'm using JBoss 4.2.2 GA and Seam 2.0

              • 4. Re: Multiple DataModels scoped to PAGE context, only 1 Factory called when returning to page
                jimk1723

                If populateList2 is not getting called, what does the list2 context variable contain after you return from toggle.toggleButton? The old list?


                The Factory will only get called if there is no value for the context variable 'list2' - is there a 'list2' defined in a higher scope?


                You might try adding a 'org.jboss.seam.postSetVariable.list2' event observer to see when that context variable is getting set.


                • 5. Re: Multiple DataModels scoped to PAGE context, only 1 Factory called when returning to page
                  alain94040

                  I have the exact same problem: two DataModel on scope of PAGE. The first time the page is viewed by the user, both Factory methods are called.


                  The second time the page is viewed, after navigating elsewhere, only the first Factory method is called. Not the second one.

                  • 6. Re: Multiple DataModels scoped to PAGE context, only 1 Factory called when returning to page
                    alain94040

                    PS: I'm using Seam 2.0.2-SP1, so that's as recent as I can get right now.


                    Alain.

                    • 7. Re: Multiple DataModels scoped to PAGE context, only 1 Factory called when returning to page

                      Hi Alain,


                      I didn't have time to spend figuring this out so I abandoned the DataModel and just created a list with getters and setters.  It's not elegent and likely adds extra db calls (at least in my case), but it works.  My gut feeling on this is that it's probably a SEAM bug, but I don't know how to escalate things to get the dev team to look at it.

                      • 8. Re: Multiple DataModels scoped to PAGE context, only 1 Factory called when returning to page
                        alain94040

                        Hi,


                        This is annoying indeed. I guess the developers would love a testcase, but you already pretty much provided that.


                        The fact that two of us have the same problem is a sign that either:


                        1) there is a bug


                        2) there is a user error that is common enough that multiple people fall in the same trap, therefore it deserves the same attention as a bug


                        Alain.

                        • 9. Re: Multiple DataModels scoped to PAGE context, only 1 Factory called when returning to page
                          admin.admin.email.tld

                          open a JIRA issue.

                          • 10. Re: Multiple DataModels scoped to PAGE context, only 1 Factory called when returning to page
                            jackhopes

                            Here's the example I whipped up using a simple application-scoped list of POJOS in lieu of an entity. Toggle works, the lists update fine. Make Money Surveys