]
Brian Leathem updated RF-13703:
-------------------------------
Fix Version/s: 4.5-Tracking
Nested UIDataAdaptor restoring stale data because of reentrancy
problem
-----------------------------------------------------------------------
Key: RF-13703
URL:
https://issues.jboss.org/browse/RF-13703
Project: RichFaces
Issue Type: Bug
Security Level: Public(Everyone can see)
Affects Versions: 4.3.1
Environment: (Windows 7. Java 6. Jetty 7.6.9 or Tomcat 6.0.37)
Reporter: Yannick Valot
Fix For: 4.5-Tracking
I am encountering a reentrancy problem with nested UIDataAdaptor components. For instance
something like :
{code}
<rich:dataTable id="outer" ...>
<rich:dataTable id="inner" ...>
<h:inputText id="myText" ...>
</rich:dataTable>
</rich:dataTable>
{code}
When a view contains nested UIDataAdaptor components, each UIDataAdaptor component has
its own internal store for saving the saved state of child components
(saveChildState/restoreChildState). Since "inputText" is included in both
tables, each one may save and restore the value of the component in its internal store.
For instance, when "outer" is on key 0 and inner is on key "2", both
"outer" and "inner" may store the state values for myText
"outer:0:inner:2:myText" in their "childState" property.
I guess that most of the time, only "inner" will actually store useful values,
because most of calls on "setRowKey" for outer will be done when
"inner" has its rowkey set to null. Unfortunately, this is not always the case.
For instance, if I decide to visit the component tree while processing an event for
"myText", outer will perform a setRowKey while inner has its own rowKey to a
significant value (for instance, 2), the result being that inner's value will be
erased by
outer's.
To try and explain with a little more detail... Let's say that we are processing an
event on "outer:1:inner:0:myText", and we start a (nested) visit of the tree :
1) "outer" will at some point perform a setRowKey(0)
2) It will then do a restoreChildState for myText. since "inner" has rowKey set
to 0, it will restore "outer's" value for
"outer:0:inner:0:myText".
3) A bit later on during the visit, inner will do "setRowKey(0)". It will first
save the current value for "outer:0:inner:0:myText" (which comes from
outer's backup of it, restored on step 2)
The result of this process is that "outer"'s backup of
"outer:0:inner:0:myText" overwrites "inner"'s backup for it. This
is problematic because this leads, sometimes, to an old and obsolete backup
of state taking place of the current value. This may happen several HTTP requests later.
I've found a solution for this problem by not storing the saved state in the
UIDataAdaptor, but in the target component itself. In this manner, there can only be one
backup of the data. Such a modification is actually hinted at in UIDataAdaptor's
source code : "// TODO - use local map - children save their state themselves using
visitors", although my modification is simpler
(it's still UIDataAdaptor that saves the state, in the same manner, only in a
different place).
This bug is quite complex and extracting a testcase will take some time, but I am willing
to do so if requested. I can also offer my patch for the problem once I have finalized it.