5 Replies Latest reply on Jun 10, 2011 7:04 AM by pablo53

    RF4 critical bug: <rich:dataTable> inside another <rich:dataTable> or <h:dataTable> does not update its components' values.




      After many hours of debugging some issue inside my application I found a critical bug (not to say blocker) in RF-4.0.0.Final.


      Let's assume we have an array of 3 objects, and each object has (among other fields) an array of 2 objects. Now, let's try to render it on "tables-in-table" basis. Let's have a rich:dataTable with value attribute set to the "array of 3" (say, "outer table"). Then, let's have another rich:dataTable (say, "inner table") inside a rich:colulm of the outer table with value set to a field pointing to the "array of 2".


      The above will render the data correctly, but if we try to change any data inside the inner table (except the last inner table - i.e. the one inside the last row of the outer table) we will fail.



      This is an example:



      public class MyBean implements java.io.Serializable {

          // ...

          public static class Test007a implements java.io.Serializable {

              private Date myDateA = null;

              public Date getMyDateA() { return this.myDateA; }

              public void setMyDateA(Date myDateA) { this.myDateA = myDateA; }


          public static class Test007 implements java.io.Serializable {

              private Date myDate = null;

              private java.util.List<Test007a> mySubDates = new java.util.ArrayList<Test007a>();

              public Test007() { mySubDates.add(new Test007a()); mySubDates.add(new Test007a()); }

              public Date getMyDate() { return this.myDate; }

              public void setMyDate(Date myDate) { this.myDate = myDate; }

              public java.util.List<Test007a> getMySubDates() { return mySubDates; }

              public void setMySubDates(java.util.List<Test007a> mySubDates) { this.mySubDates = mySubDates; }


          private Test007[] lll = new Test007[] { new Test007(), new Test007(), new Test007() };

          public Test007[] getLll() { return this.lll; };



      and a fragment of XHTML:


      <rich:dataTable value="#{myBean.lll}" var="mm">


          <rich:column><rich:calendar value="#{mm.myDate}" datePattern="yyyy-MM-dd" enableManualInput="true" /></rich:column>


              <rich:dataTable value="#{mm.mySubDates}" var="mm2">


                  <rich:column><rich:calendar value="#{mm2.myDateA}" datePattern="yyyy-MM-dd" enableManualInput="true" /></rich:column>





      Try to fill all the calendars. You will find that the calendars in the inner tables, except the inner table in the last row of the outer table, loose their values after postbacks. If we change <rich:dataTable> and <rich:column> to <h:dataTable> and <h:column> (respectively) everything starts to work as expected. Mixing outer "rich:" and inner "h:" versions make things even worse - ale the inner tables have the same client ids in the corresponding fields (at least, this is what I noticed in Mojarra 2.1.1).


      To sum up, the above example show a serious bug in RF 4.0 (running on Mojarra 2.0.4 and 2.1.1; other versions of Mojarra are also suspected, but I have not tested them).





      P.S. Sorry for the variable names in the example.

        • 1. Re: RF4 critical bug: <rich:dataTable> inside another <rich:dataTable> or <h:dataTable> does not update its components' values.

          I'm trying to investigate the issue. I am not familiared with RF source code, but I have found the following:


          1) <rich:calendar> does not loose its values inside the last subtable, but other components do - e.g. <h:inputText>,

          2) the decoding phase seems to find the values sent by a browser but updating process of the calendar component breaks in UIInput.updateModel() because it sees localValue not being set (RichFaces' UICalendar derives updateModel from its supersuperclass UIInput).


          It seems as if there were some problems when iterating through the rich:dataTable's rows...

          • 2. Re: RF4 critical bug: <rich:dataTable> inside another <rich:dataTable> or <h:dataTable> does not update its components' values.

            I think I found where the bug of RF 4.0.0 is.


            <rich:dataTable> component's underlying object is a subclass of org.richfaces.component.UIDataAdaptor, which, in turn, implements org.ajax4jsf.component.IterationStateHolder. When iterating through the rows, each row change means: 1) saving state of the "old" row, 2) changing row index to the new one and 3) restoring state of the "new" row. This is obvious, since all the JSF sequence-like components do it the same way. As a convention, there is only one instance of each "iterating" object, so the method of "save-next-restore" is the only possible way to iterate.


            The interface org.ajax4jsf.component.IterationStateHolder contracts two methods for getting and setting current iteration state. I emphasize, THE CURRENT ITERATION. The method saveChildState of org.richfaces.component.UIDataAdaptor save the state of its child passed as a parameter. This method checks whether the child implements org.ajax4jsf.component.IterationStateHolder. If so, it creates a new instance of org.richfaces.componen.SavedState with only one field set: iterationState. Since the "inner" table iteration has been finished at that moment (row index = -1), the iterationState field contains the information of out-of-the-rows components of the "inner" table. It does not contain any information of the components instances related to any "real" row. This way, the inner table (iterated) components' state are lost.


            To sum up:


            1) The docode process finds the values sent by the browser correctly.

            2) When UIDataAdaptor component (e.g. rich:dataTable) contains children implementing IterationStateHolder (again, rich:dataTable is an example of such) its iteration process looses children state on each row change. hence, after the decode phase, all the sent values are lost, and cannot be applied later in the life cycle.




            Would any developer guy confirm that my investigation is right? Whom post the above bug to?





            • 3. Re: RF4 critical bug: <rich:dataTable> inside another <rich:dataTable> or <h:dataTable> does not update its components' values.

              Hi Pawel,


              I guesss it's the same problem as https://issues.jboss.org/browse/RF-10859 - please take a look.

              • 4. Re: RF4 critical bug: <rich:dataTable> inside another <rich:dataTable> or <h:dataTable> does not update its components' values.

                Thanks, Nick!


                You are right - the problem is the same as for nesting <a4j:repeat>. Setting keepSaved="true" for <rich:dataTable> seems to workaround the issue as well. <rich:calendar> started keeping its values across postbacks...


                The strange thing I have just discovered is that <h:inputText> does not work as expected, even inside a hierarchy of <h:dataTables>s. So, this must be some bug of Mojarra 2.1.1-b04 (I suspect that earlier versions of Mojarra have this bug too, but I have not tested them).




                • 5. Re: RF4 critical bug: <rich:dataTable> inside another <rich:dataTable> or <h:dataTable> does not update its components' values.

                  Another issue has aroused.


                  Let's assume that the inner table has 3 rows based on some data model (e.d. ArrayDataModel). If we delete the 2nd one, we will stiil see the data from the 1st and 2nd, however we should se the data form the 1st and 3rd. It seems that, no matter which row we delete from the data model, the only effect is that we see all the rows but last after postback. The data model has proper data, but components show "sticky" values - not corresponding to the model.


                  I think keepSaved="true" is not the right solution if the data model inserts and deletes row dynamically.


                  Will upgrading to RF 4.0.1-SNAPSHOT or RF 4.1.0-SNAPSHOT resolve the problem?