You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@myfaces.apache.org by Mathias Stein <ms...@gmx.de> on 2006/01/19 23:08:17 UTC

concurrency and t:dataTable (preserveDataModel, forceId, param)

I'm trying to implement a simple DataTable with a button on each row.
A click on one of the buttons should delete the corresponding row item.
In a singleuser environment my application works as expected.
But up to now I haven't found a proper soultion for multiuser environments.

The DataTable has a value binding to a list property of a backing bean.
    <h:dataTable value="#{backingBean.allItems}" var="item" >
    ...
       <h:commandButton action="#{item.doDelete}" immediate="true" />
    ...
The property getter getAllItems() queries the items list for example
from a data base. In a multiuser environment the getter getAllItems()
could return a different list every time it is called.


Now my problem:
In case the data base was changed since the last view rendering, the
method doDelete() is called on the wrong item object.

For example:
If I select item3 for deletion in my browser and someone else meanwhile
deleted itemx x<3, my submission deletes item4 instead of item3.


Approach 1: (cached items list, no refresh)
The property getter getAllItems() could query the list only once from
the data base and store the list in the session for further evaluations.
So I always delete the correct item.
But in getAllItems() I'm unable to decide when to update the stored
list. Therefore the stored list stays constant for the whole session
unless I update it manually.


Approach 2: (tomahawk preserveDataModel)
Using <t:dataTable.. with preserveDataModel="true" as described in [1]
seemed to solve the problem at first glance.
The DataModel (my item list) is stored inside the view and restored on
request processing. So when someone else changed the data base
meanwhile, my DataTable instance still uses the old item list, and the
correct item gets deleted.
But I noticed, that the current implementation of t:dataTable calls my
property getter getAllItems() twice per request:
    firstly for rendering the html view
    and secondly to store the DataModel.
Although the risk for deleting the wrong item is reduced tremendously
now, it still remains a small probability, that the data base is changed
between both calls of getAllItems().
Therefore "tomahawk preserveDataModel" doesn't seem to be a real
solution to the problem either.


Approach 3: (t:commandButton and forceId)
Since the mistaken deletes are a consequnce of the weak id generation, I
tried to use the data base primary key as part of the jsf commandButton id:
     <t:commandButton action="#{item.doDelete}" immediate="true"
         forceId="true" forceIdIndex="false" id="#{item.myid}" />
But the id attribute unfortunately has to be a static value.


Approach 4: (h:commandLink, f:param and getRequestMap)
When you replace the commandButton by a commandLink, you can add a row
specific parameter to the request. In the action method you can extract
the parameter from the requestMap and use it as item primary key.
Since commandLinks need JavaScript, this hack is no solution either.


I'm quite new to JSF and the problem seems very common to me, so there's
probably a simple solution. Please let me know...


thx
Mathias


[1]
http://wiki.apache.org/myfaces/Working_With_DataTable_And_PreserveDataModel





Re: AW: concurrency and t:dataTable (preserveDataModel, forceId, param)

Posted by Simon Kitching <sk...@apache.org>.
On Sun, 2006-01-22 at 14:21 +0100, Matthias Kahlau wrote:
> > > DataTable doesn't store the data model because
> ...
> > > The t:dataTable does have attribute "preserveDataModel" which does
> > > preserve the data model.
> 
> 
> How does t:dataTable actually preserve the data model, when it doesn't store
> it?

It does store it when preserveDataModel is set. This requires the
wrapped data in the datamodel to be serializable.

I was pointing out why this isn't the *default* for h:dataTable or
t:dataTable.

Regards,

Simon


AW: concurrency and t:dataTable (preserveDataModel, forceId, param)

Posted by Matthias Kahlau <mk...@web.de>.
> > DataTable doesn't store the data model because
...
> > The t:dataTable does have attribute "preserveDataModel" which does
> > preserve the data model.


How does t:dataTable actually preserve the data model, when it doesn't store
it?


Regards,

Matthias

> -----Ursprüngliche Nachricht-----
> Von: users-return-15771-mkahlau=web.de@myfaces.apache.org
> [mailto:users-return-15771-mkahlau=web.de@myfaces.apache.org]Im Auftrag
> von Martin Marinschek
> Gesendet: Sonntag, 22. Januar 2006 11:33
> An: MyFaces Discussion; skitching@apache.org
> Betreff: Re: concurrency and t:dataTable (preserveDataModel, forceId,
> param)
>
>
> Hold on - t:dataTable refetches the data before rendering, right?
>
> regards,
>
> Martin
>
> On 1/22/06, Simon Kitching <sk...@apache.org> wrote:
> > On Fri, 2006-01-20 at 14:26 +0100, Mathias Stein wrote:
> > >  > The data list used during validation phase *must* be the
> same as the one
> > >  > used during the previous rendering phase, otherwise as you note the
> > >  > wrong object can be accessed.
> > >
> > > As fas as I know the view is serialized, stored in the session and
> > > deserialized on request submition.
> > > Why doesn't dataTable simply store the data model or at least the row
> > > bindings automatically?
> >
> > DataTable doesn't store the data model because
> > (a)
> > There is no guarantee that the data model is serializable. It's a
> > user-provided object. And in addition, serializing/deserializing is not
> > always the same as keeping the original object in the http session; an
> > example is Hibernate entities, where serialize/deserialize results in
> > the objects becoming "detached".
> > (b)
> > Storing it could make the saved state *much* larger. Ok, sometimes it's
> > necessary but the code shouldn't default to doing this..
> > (c)
> > It's consistent with the behaviour for other objects. An h:inputText
> > component for example will call its property getter during the validate
> > phase to determine whether the newly submitted value is different from
> > the model value (ie whether a ValueChangeEvent should be queued).
> >
> > The t:dataTable does have attribute "preserveDataModel" which does
> > preserve the data model. However it keeps the data for *too* long (ie
> > doesn't refetch before render). Maybe t:dataTable should have another
> > variant that preserves data from render to postback, but then clears
> > it?
> >
> > > This concurrency issue seems to me like a standard problem of
> all table
> > > based web applications.
> >
> > Yes it is.
> >
> > > Is this a problem with the specification or with the implementations?
> > > By the way, myfaces and Suns RI show exacly the same behavior.
> >
> > The refetch is behaviour required by the spec.
> >
> > Regards,
> >
> > Simon
> >
> >
>
>
> --
>
> http://www.irian.at
>
> Your JSF powerhouse -
> JSF Consulting, Development and
> Courses in English and German
>
> Professional Support for Apache MyFaces


Re: concurrency and t:dataTable (preserveDataModel, forceId, param)

Posted by Martin Marinschek <ma...@gmail.com>.
Hold on - t:dataTable refetches the data before rendering, right?

regards,

Martin

On 1/22/06, Simon Kitching <sk...@apache.org> wrote:
> On Fri, 2006-01-20 at 14:26 +0100, Mathias Stein wrote:
> >  > The data list used during validation phase *must* be the same as the one
> >  > used during the previous rendering phase, otherwise as you note the
> >  > wrong object can be accessed.
> >
> > As fas as I know the view is serialized, stored in the session and
> > deserialized on request submition.
> > Why doesn't dataTable simply store the data model or at least the row
> > bindings automatically?
>
> DataTable doesn't store the data model because
> (a)
> There is no guarantee that the data model is serializable. It's a
> user-provided object. And in addition, serializing/deserializing is not
> always the same as keeping the original object in the http session; an
> example is Hibernate entities, where serialize/deserialize results in
> the objects becoming "detached".
> (b)
> Storing it could make the saved state *much* larger. Ok, sometimes it's
> necessary but the code shouldn't default to doing this..
> (c)
> It's consistent with the behaviour for other objects. An h:inputText
> component for example will call its property getter during the validate
> phase to determine whether the newly submitted value is different from
> the model value (ie whether a ValueChangeEvent should be queued).
>
> The t:dataTable does have attribute "preserveDataModel" which does
> preserve the data model. However it keeps the data for *too* long (ie
> doesn't refetch before render). Maybe t:dataTable should have another
> variant that preserves data from render to postback, but then clears
> it?
>
> > This concurrency issue seems to me like a standard problem of all table
> > based web applications.
>
> Yes it is.
>
> > Is this a problem with the specification or with the implementations?
> > By the way, myfaces and Suns RI show exacly the same behavior.
>
> The refetch is behaviour required by the spec.
>
> Regards,
>
> Simon
>
>


--

http://www.irian.at

Your JSF powerhouse -
JSF Consulting, Development and
Courses in English and German

Professional Support for Apache MyFaces

Re: preserveDataModel reloaded

Posted by Simon Kitching <sk...@apache.org>.
On Mon, 2006-01-23 at 15:33 +0100, Mathias Stein wrote:
> What t:dataTable really does at the moment, is:
> 1) In rendering phase getAllItems() is called the first time.
> 2) If preserveDataModel is enabled, t:dataTable calls getAllItems() 
> !!!one more time!!! to save the data model.
[snip]
> But if the dataModel changes between step 1 and 2, the previous 
> rendering phase still uses the old version, while t:dataTable stores and 
> restores already the changed one. So preserveDataModel only reduces the 
> risk. The problem is shifted between step 1 and 2.
> 
> I think step 1 and 2 should call getAllItems() only once per request,
> and they should use exactly the same data model for rendering and for 
> storing.
> 
> Please correct me, if I'm wrong.

No, if things do indeed work as you describe then I agree that would be
a bug. Please create a JIRA issue for this.

Of course I have no idea how easy it would be to fix that issue...

Regards,

Simon


preserveDataModel reloaded

Posted by Mathias Stein <ms...@gmx.de>.
Simon Kitching wrote:

 >The t:dataTable does have attribute "preserveDataModel" which does
 >preserve the data model. However it keeps the data for *too* long (ie
 >doesn't refetch before render). Maybe t:dataTable should have another
 >variant that preserves data from render to postback, but then clears
 >it?

Hi Simon,

I think we are not talking about the same problem here. Beside the general
problem of h:dataTable and the spec, I think there's a simple bug in the
implementation of t:dataTable. Perhaps my description of approach 4 was
ambiguous. So let me explain again in detail:

In my opinion perserveDataModel should guarantee the following constraint:
    "dataModel(previous rendering phase) == dataModel(postback)"

(see also [1])

What t:dataTable really does at the moment, is:
1) In rendering phase getAllItems() is called the first time.
2) If preserveDataModel is enabled, t:dataTable calls getAllItems() 
!!!one more time!!! to save the data model.

...some minutes later the user submits the form

3) In the postback t:dataTable restores the data model from the 
serialized data (step 2) instead of calling getAllItems().
So the current validation phase uses probably the same data as in the 
previous rendering phase.

Problem:
But if the dataModel changes between step 1 and 2, the previous 
rendering phase still uses the old version, while t:dataTable stores and 
restores already the changed one. So preserveDataModel only reduces the 
risk. The problem is shifted between step 1 and 2.

I think step 1 and 2 should call getAllItems() only once per request,
and they should use exactly the same data model for rendering and for 
storing.

Please correct me, if I'm wrong.

thx,
Mathias


[1] 
http://wiki.apache.org/myfaces/Working_With_DataTable_And_PreserveDataModel


Re: concurrency and t:dataTable (preserveDataModel, forceId, param)

Posted by Simon Kitching <sk...@apache.org>.
On Fri, 2006-01-20 at 14:26 +0100, Mathias Stein wrote:
>  > The data list used during validation phase *must* be the same as the one
>  > used during the previous rendering phase, otherwise as you note the
>  > wrong object can be accessed.
> 
> As fas as I know the view is serialized, stored in the session and 
> deserialized on request submition.
> Why doesn't dataTable simply store the data model or at least the row 
> bindings automatically?

DataTable doesn't store the data model because
(a)
There is no guarantee that the data model is serializable. It's a
user-provided object. And in addition, serializing/deserializing is not
always the same as keeping the original object in the http session; an
example is Hibernate entities, where serialize/deserialize results in
the objects becoming "detached".
(b)
Storing it could make the saved state *much* larger. Ok, sometimes it's
necessary but the code shouldn't default to doing this..
(c)
It's consistent with the behaviour for other objects. An h:inputText
component for example will call its property getter during the validate
phase to determine whether the newly submitted value is different from
the model value (ie whether a ValueChangeEvent should be queued).

The t:dataTable does have attribute "preserveDataModel" which does
preserve the data model. However it keeps the data for *too* long (ie
doesn't refetch before render). Maybe t:dataTable should have another
variant that preserves data from render to postback, but then clears
it? 

> This concurrency issue seems to me like a standard problem of all table 
> based web applications.

Yes it is.

> Is this a problem with the specification or with the implementations?
> By the way, myfaces and Suns RI show exacly the same behavior.

The refetch is behaviour required by the spec.

Regards,

Simon


Re: concurrency and t:dataTable (preserveDataModel, forceId, param)

Posted by Mathias Stein <ms...@gmx.de>.
 > The data list used during validation phase *must* be the same as the one
 > used during the previous rendering phase, otherwise as you note the
 > wrong object can be accessed.

As fas as I know the view is serialized, stored in the session and 
deserialized on request submition.
Why doesn't dataTable simply store the data model or at least the row 
bindings automatically?
This concurrency issue seems to me like a standard problem of all table 
based web applications.
Is this a problem with the specification or with the implementations?
By the way, myfaces and Suns RI show exacly the same behavior.


 >But the list *must* be refetched before the render phase if you don't
 >want to render stale data.

Okay it must be refetched before the render phase.
But why is it refetched before calling the action?


 >There was a thread on creating a "PhaseTracker" just yesterday or the
 >day before; see the user list email archives.
 >
 >This doesn't seem very elegant, but it's the best I've found. Any better
 >suggestions are very welcome.

Thanks a lot for your solution. I'll try as soon as possible.


 >> Approach 3: (t:commandButton and forceId

 > This won't work anyway because if it wasn't a static value then the
 > button for row N of the table would just be taking the id from the Nth
 > element in the data list at postback - and therefore using the wrong
 > record key if the datalist at postback != datalist at render.

Is there any other way to use user defined row ids like item primary keys?
By the way, user defined row ids could help to solve problems
with the browsers back button, too.


tanks so far
Mathias


Re: concurrency and t:dataTable (preserveDataModel, forceId, param)

Posted by Simon Kitching <sk...@apache.org>.
On Thu, 2006-01-19 at 23:08 +0100, Mathias Stein wrote:
> I'm trying to implement a simple DataTable with a button on each row.
> A click on one of the buttons should delete the corresponding row item.
> In a singleuser environment my application works as expected.
> But up to now I haven't found a proper soultion for multiuser environments.
> 
> The DataTable has a value binding to a list property of a backing bean.
>     <h:dataTable value="#{backingBean.allItems}" var="item" >
>     ...
>        <h:commandButton action="#{item.doDelete}" immediate="true" />
>     ...
> The property getter getAllItems() queries the items list for example
> from a data base. In a multiuser environment the getter getAllItems()
> could return a different list every time it is called.

Yep, it's a problem. There's another minor variation you don't list:
using t:saveState to store the datamodel's list, which I would recommend
over session-scope beans, but the issue's the same.

The data list used during validation phase *must* be the same as the one
used during the previous rendering phase, otherwise as you note the
wrong object can be accessed. 

But the list *must* be refetched before the render phase if you don't
want to render stale data.

The best solution I've found is to create a class to track the current
JSF phase which code can consult to know what the current JSF phase is.
Use a request-scoped backing bean. In the datamodel getter method, if
the previous get was not in the render phase, but the current get is in
the render phase then refetch the List of data. Otherwise, return the
cached data. Use a t:saveState tag in the page to store the List of
data, so on restore view the old data list is injected into the backing
bean and thus available to correctly interpret the postback.

There was a thread on creating a "PhaseTracker" just yesterday or the
day before; see the user list email archives.

This doesn't seem very elegant, but it's the best I've found. Any better
suggestions are very welcome.


> 
> Approach 3: (t:commandButton and forceId)
> Since the mistaken deletes are a consequnce of the weak id generation, I
> tried to use the data base primary key as part of the jsf commandButton id:
>      <t:commandButton action="#{item.doDelete}" immediate="true"
>          forceId="true" forceIdIndex="false" id="#{item.myid}" />
> But the id attribute unfortunately has to be a static value.

This won't work anyway because if it wasn't a static value then the
button for row N of the table would just be taking the id from the Nth
element in the data list at postback - and therefore using the wrong
record key if the datalist at postback != datalist at render.


Regards,

Simon