You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@myfaces.apache.org by Volker Weber <v....@inexso.de> on 2014/04/10 17:05:17 UTC

RestoreView Problem after dynamically added component

Hi,

we are facing a duplicate id exception after adding a component in
applicationPhase to to view.

We are using c:forEach and add an item to the iterated list in the
actionListener.

Saving this view is done without problem, but restoring this saved view
results in a view containing the new component twice! Which results in a
duplicate id exception when saving this restored view again.

I found that this new component on creation is marked as
"oam.COMPONENT_ADDED_AFTER_BUILD_VIEW.
On restore view the facelet is used to recreate the view, which is
including the new component because it is now included in the forEach
iteration.
After recreating the view with the facelet the view is extended with the
components which were marked with "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW
when storing the view.

At this point the component is duplicated in the view.

The Problem is in
org.apache.myfaces.view.facelets.DefaultFaceletsStateManagementStrategy
(row 437 to 451 in 2.1.10 sources), the component is added to the view
regardless if there is already a component with same id.

I have a example app to reproduce this Problem can downloaded
herehttp://www.inexso.net/files/myfaces-dynamic-test.zip .

Run this with "mvn jetty:run" to reproduce this.

Running this with "mvn -Dcontainer=jetty-mojarra jetty:run" did not give
me any error.


Regards,
  Volker


-- 
Volker Weber
-Leiter Software-Entwicklung-

inexso - information exchange solutions GmbH
Ofener Str. 30      | 26121 Oldenburgwww.inexso.de

Firmensitz: Oldenburg | Amtsgericht Oldenburg HRB 205251
Geschäftsführer: Stefan Schulte, Michael Terschüren

Re: RestoreView Problem after dynamically added component

Posted by Leonardo Uribe <lu...@gmail.com>.
Hi

2014-04-14 10:20 GMT+02:00 Volker Weber <v....@inexso.de>:
> Hi Leonardo,
>
> thanks for the detailed explanation.
>
> I disabling the PPS  for now.
> We are using tobago where the problem occurs, and because tobago did not use
> PPS in the partial rendering implementation this could easily disabled.
>

Ok. Anyway I have fixed it in:

https://issues.apache.org/jira/browse/MYFACES-3883

regards,

Leonardo

> Regards,
>     Volker
>
>
>
> 2014-04-11 17:11 GMT+02:00 Leonardo Uribe <lu...@gmail.com>:
>
>> Hi
>>
>> The problem of c:forEach is a long story. I'll try to make a summary. This
>> tag
>> originally came from JSP, and then it was somewhat "copied" into facelets,
>> together with other tags. But the copy in facelets had a problem: it
>> doesn't
>> store the "model" internally (JSP variant does). Instead, the model is
>> reapplied each time a refresh over the page is done, usually before render
>> response phase by facelets. The effect of the refresh is usually remove or
>> add the necessary components to synchronize the model. Since in JSF 1.2
>> the
>> whole component tree is saved in the state, nobody noticed anything. Well,
>> some guys saw the problem. See:
>>
>>
>> http://drewdev.blogspot.co.at/2008/08/cforeach-with-jsf-could-ruin-your-day.html
>>
>> Andrew Robinson did a partial solution in tr:forEach, but I'm afraid the
>> only solution that will work reliable is fix c:forEach.
>>
>> But with the introduction of JSF 2 Partial State Saving (PSS) algorithm,
>> it
>> was changed the way how the component tree was saved and restored. This
>> introduced some new problems:
>>
>> 1. The generated ids were not stable enough in JSF 1.2, and PSS requires
>> stable ids, which in other words means the same ids are generated to the
>> component each time facelets algorithm is applied. Content generated by
>> c:forEach is not stable enough, so you have situations where the state
>> just get lost.
>>
>> 2. Now the component state is split in two: the initial state and the
>> delta
>> state, but c:forEach is a facelet tag that is not bound to a component
>> counterpart, so it doesn't have an associated state to be split.
>>
>> 3. In JSF 1.2 it was only one refresh point (before render response), but
>> JSF 2 PSS added one extra point where facelets algorithm is executed
>> (restore view phase). Please note if PSS is disabled, this is not called.
>>
>> The final result is we have not just one problem but a set of different
>> problems all over the same facelet tag. We finally solved the problem in
>> MyFaces 2.2.2 after a lot of struggle. The code could be backport into
>> 2.1.x,
>> but at this stage it was considered too risky, because it can introduce
>> instability to existing applications. We can consider the backport but
>> only
>> after some more releases of 2.2.x branch.
>>
>> There is a set of well known workarounds, so you can just apply one of
>> them
>> and problem solved. It is too hard to explain in detail every change done
>> in
>> the algorithm, it took a lot of time and effort, but the important thing
>> to
>> remind is the new code in 2.2.2 copies the model as in JSP to create the
>> initial state. So, the model needs to be Serializable and if it is not, it
>> fallback to the old algorithm, so that's why the code works after you make
>> DynamicTab implements Serializable interface. That's the recommended
>> solution.
>>
>> Why it works in Mojarra? it doesn't work, it apparently work, but it
>> doesn't.
>> Try this change:
>>
>>   <h:panelGroup>
>>   <h:inputText value="begin"/>
>>   <c:forEach items="#{tabProvider.tabs}" var="tab">
>>     <h:inputText value="#{tab.outValue}"/>
>>     <br/>
>>   </c:forEach>
>>   <h:inputText value="end"/>
>>   </h:panelGroup>
>>
>> The generated markup is this (Mojarra 2.1.28):
>>
>> <input type="text" name="j_idt6" value="begin" />
>>     <input type="text" name="j_idt7" value="Content of dynamic Tab number
>> 0" />
>>     <br />
>>     <input type="text" name="j_idt9" value="Content of dynamic Tab number
>> 1" />
>>     <br />
>>     <input type="text" name="j_idt11" value="Content of dynamic Tab number
>> 2" />
>>     <br />
>> <input type="text" name="j_idt13" value="end" />
>>
>> Can you see the problem? The id generation is completely broken! Why?
>> because you can put a c:if before the c:forEach and change the way how the
>> ids
>> are generated, and in that way alter the state and have some nasty
>> exception
>> like duplicate ids or "state get lost" problems. That's a "Chronicle of a
>> Death Foretold". If you make a click on the button, you'll see this:
>>
>> <input type="text" name="j_idt6" value="begin" />
>>     <input type="text" name="j_idt7" value="Content of dynamic Tab number
>> 0" />
>>     <br />
>>     <input type="text" name="j_idt9" value="Content of dynamic Tab number
>> 1" />
>>     <br />
>>     <input type="text" name="j_idt11" value="Content of dynamic Tab number
>> 2" />
>>     <br />
>>     <input type="text" name="j_idt15" value="Content of dynamic Tab number
>> 3" />
>>     <br />
>> <input type="text" name="j_idt13" value="end" />
>>
>> Yes, it works, but that suggest they are internally doing a fallback to
>> JSF 1.2
>> state saving over that portion, which is just the same thing as we do with
>> org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS_PRESERVE_STATE. This has
>> obviously a cost over the state size.
>>
>> The problem is a fallback to JSF 1.2 is not a satisfactory solution.
>> We have already
>> heard about that. State gets bigger, slow performance, because the
>> implementation
>> needs to make complex calculations over the state, components that loses
>> the
>> state, unexpected exceptions of all kinds, that nobody knows how to
>> reproduce it
>> and how to fix them. It could work in the simple cases, but if you
>> start to add more
>> and more things to the page, sooner or later the page will break.
>>
>> If you don't believe me take a look at this error:
>>
>> https://java.net/jira/browse/JAVASERVERFACES-3241
>>
>> With that report only, not even the wizard Mandrake can know what the
>> hell happened.
>> This is not a rant. This is "old history", something that we already
>> realized many years
>> ago. See this mail:
>>
>>
>> https://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2011-10/message/2
>>
>> Now what we know what is wrong with c:forEach and that there are
>> workarounds for
>> this stuff, we can ask the big question:
>>
>> Why the same code fails in MyFaces?
>>
>> It fails because MyFaces applies PSS algorithm the way it supposed to be
>> by
>> JSF spec. When you add a new tab, the component is marked to be restored
>> fully, so the first time it doesn't happen anything, but the new tab is
>> saved fully into the state, but when the algorithm restores the view, it
>> tries to calculate the initial state and then it add the added tab, but
>> it was added twice because the initial state changed, and one of the base
>> principles is that the initial state does not change across requests. The
>> underlying problem is c:forEach should store the model of the first
>> request
>> processed so PSS algorithm can still be valid. But that can only be done
>> if
>> the model is Serializable.
>>
>> So, theoretically the default mode of PSS algorithm should implement the
>> solution proposed and already added in MyFaces 2.2.2. From the beginning
>> we have known that PSS algorithm is not 100% fail safe, but we also know
>> the only remaining case is this one.
>>
>> Can we fix this case? even if it is not the best solution?
>>
>> What we can do is add some lines of code that checks for the duplicate
>> when
>> the component is added and use the saved version of the component instead.
>> More than a fix is a "damage control" alternative. But it sounds good,
>> because the check can be done quickly in the nearby, where the component
>> is supposed to be, and you can activate the case, just checking if the
>> component is managed by facelets.
>>
>> I'll try the fix and if works, I'll commit it on all branches (2.0.x,
>> 2.1.x
>> and 2.2.x).
>>
>> regards,
>>
>> Leonardo
>>
>> 2014-04-11 14:06 GMT+02:00 Leonardo Uribe <lu...@gmail.com>:
>> > Hi
>> >
>> > It seems the example works under this configurations on 2.1.x branch:
>> >
>> > 1. Set javax.faces.PARTIAL_STATE_SAVING to false.
>> >
>> > or
>> >
>> > 2. Add the page in javax.faces.FULL_STATE_SAVING_VIEW_IDS (disable PSS
>> > on the related page only).
>> >
>> > or
>> >
>> > 3. Set org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS_PRESERVE_STATE
>> > to true.
>> >
>> > The reason is simple, in all previous cases what we are doing is mark
>> > that part of the tree to be saved fully. I'll give a detailed
>> > explanation later, I just wanted to give the workarounds for the time
>> > being.
>> >
>> > regards,
>> >
>> > Leonardo Uribe
>> >
>> >
>> > 2014-04-10 19:21 GMT+02:00 Leonardo Uribe <lu...@gmail.com>:
>> >> Hi
>> >>
>> >> I was able to reproduce the problem. But I can confirm the problem is
>> >> gone once you use 2.2.3 and make DynamicTab implements Serializable.
>> >> I'll try to find if there is a way to fix it somehow in 2.1.x, so it
>> >> can be "less broken".
>> >>
>> >> regards,
>> >>
>> >> Leonardo
>> >>
>> >> 2014-04-10 17:05 GMT+02:00 Volker Weber <v....@inexso.de>:
>> >>> Hi,
>> >>>
>> >>> we are facing a duplicate id exception after adding a component in
>> >>> applicationPhase to to view.
>> >>>
>> >>> We are using c:forEach and add an item to the iterated list in the
>> >>> actionListener.
>> >>>
>> >>> Saving this view is done without problem, but restoring this saved
>> >>> view
>> >>> results in a view containing the new component twice! Which results in
>> >>> a
>> >>> duplicate id exception when saving this restored view again.
>> >>>
>> >>> I found that this new component on creation is marked as
>> >>> "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW.
>> >>> On restore view the facelet is used to recreate the view, which is
>> >>> including the new component because it is now included in the forEach
>> >>> iteration.
>> >>> After recreating the view with the facelet the view is extended with
>> >>> the
>> >>> components which were marked with
>> >>> "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW
>> >>> when storing the view.
>> >>>
>> >>> At this point the component is duplicated in the view.
>> >>>
>> >>> The Problem is in
>> >>>
>> >>> org.apache.myfaces.view.facelets.DefaultFaceletsStateManagementStrategy
>> >>> (row 437 to 451 in 2.1.10 sources), the component is added to the view
>> >>> regardless if there is already a component with same id.
>> >>>
>> >>> I have a example app to reproduce this Problem can downloaded here
>> >>> http://www.inexso.net/files/myfaces-dynamic-test.zip .
>> >>>
>> >>> Run this with "mvn jetty:run" to reproduce this.
>> >>>
>> >>> Running this with "mvn -Dcontainer=jetty-mojarra jetty:run" did not
>> >>> give
>> >>> me any error.
>> >>>
>> >>>
>> >>> Regards,
>> >>>   Volker
>> >>>
>> >>>
>> >>> --
>> >>> Volker Weber
>> >>> -Leiter Software-Entwicklung-
>> >>>
>> >>> inexso - information exchange solutions GmbH
>> >>> Ofener Str. 30      | 26121 Oldenburg
>> >>> www.inexso.de
>> >>>
>> >>> Firmensitz: Oldenburg | Amtsgericht Oldenburg HRB 205251
>> >>> Geschäftsführer: Stefan Schulte, Michael Terschüren
>> >>>
>> >>>
>
>
>
>
> --
> inexso - information exchange solutions GmbH
> Ofener Straße 30 | 26121 Oldenburg
> www.inexso.de

Re: RestoreView Problem after dynamically added component

Posted by Volker Weber <v....@inexso.de>.
Hi Leonardo,

thanks for the detailed explanation.

I disabling the PPS  for now.
We are using tobago where the problem occurs, and because tobago did not
use PPS in the partial rendering implementation this could easily disabled.

Regards,
    Volker



2014-04-11 17:11 GMT+02:00 Leonardo Uribe <lu...@gmail.com>:

> Hi
>
> The problem of c:forEach is a long story. I'll try to make a summary. This
> tag
> originally came from JSP, and then it was somewhat "copied" into facelets,
> together with other tags. But the copy in facelets had a problem: it
> doesn't
> store the "model" internally (JSP variant does). Instead, the model is
> reapplied each time a refresh over the page is done, usually before render
> response phase by facelets. The effect of the refresh is usually remove or
> add the necessary components to synchronize the model. Since in JSF 1.2 the
> whole component tree is saved in the state, nobody noticed anything. Well,
> some guys saw the problem. See:
>
>
> http://drewdev.blogspot.co.at/2008/08/cforeach-with-jsf-could-ruin-your-day.html
>
> Andrew Robinson did a partial solution in tr:forEach, but I'm afraid the
> only solution that will work reliable is fix c:forEach.
>
> But with the introduction of JSF 2 Partial State Saving (PSS) algorithm, it
> was changed the way how the component tree was saved and restored. This
> introduced some new problems:
>
> 1. The generated ids were not stable enough in JSF 1.2, and PSS requires
> stable ids, which in other words means the same ids are generated to the
> component each time facelets algorithm is applied. Content generated by
> c:forEach is not stable enough, so you have situations where the state
> just get lost.
>
> 2. Now the component state is split in two: the initial state and the delta
> state, but c:forEach is a facelet tag that is not bound to a component
> counterpart, so it doesn't have an associated state to be split.
>
> 3. In JSF 1.2 it was only one refresh point (before render response), but
> JSF 2 PSS added one extra point where facelets algorithm is executed
> (restore view phase). Please note if PSS is disabled, this is not called.
>
> The final result is we have not just one problem but a set of different
> problems all over the same facelet tag. We finally solved the problem in
> MyFaces 2.2.2 after a lot of struggle. The code could be backport into
> 2.1.x,
> but at this stage it was considered too risky, because it can introduce
> instability to existing applications. We can consider the backport but only
> after some more releases of 2.2.x branch.
>
> There is a set of well known workarounds, so you can just apply one of them
> and problem solved. It is too hard to explain in detail every change done
> in
> the algorithm, it took a lot of time and effort, but the important thing to
> remind is the new code in 2.2.2 copies the model as in JSP to create the
> initial state. So, the model needs to be Serializable and if it is not, it
> fallback to the old algorithm, so that's why the code works after you make
> DynamicTab implements Serializable interface. That's the recommended
> solution.
>
> Why it works in Mojarra? it doesn't work, it apparently work, but it
> doesn't.
> Try this change:
>
>   <h:panelGroup>
>   <h:inputText value="begin"/>
>   <c:forEach items="#{tabProvider.tabs}" var="tab">
>     <h:inputText value="#{tab.outValue}"/>
>     <br/>
>   </c:forEach>
>   <h:inputText value="end"/>
>   </h:panelGroup>
>
> The generated markup is this (Mojarra 2.1.28):
>
> <input type="text" name="j_idt6" value="begin" />
>     <input type="text" name="j_idt7" value="Content of dynamic Tab number
> 0" />
>     <br />
>     <input type="text" name="j_idt9" value="Content of dynamic Tab number
> 1" />
>     <br />
>     <input type="text" name="j_idt11" value="Content of dynamic Tab number
> 2" />
>     <br />
> <input type="text" name="j_idt13" value="end" />
>
> Can you see the problem? The id generation is completely broken! Why?
> because you can put a c:if before the c:forEach and change the way how the
> ids
> are generated, and in that way alter the state and have some nasty
> exception
> like duplicate ids or "state get lost" problems. That's a "Chronicle of a
> Death Foretold". If you make a click on the button, you'll see this:
>
> <input type="text" name="j_idt6" value="begin" />
>     <input type="text" name="j_idt7" value="Content of dynamic Tab number
> 0" />
>     <br />
>     <input type="text" name="j_idt9" value="Content of dynamic Tab number
> 1" />
>     <br />
>     <input type="text" name="j_idt11" value="Content of dynamic Tab number
> 2" />
>     <br />
>     <input type="text" name="j_idt15" value="Content of dynamic Tab number
> 3" />
>     <br />
> <input type="text" name="j_idt13" value="end" />
>
> Yes, it works, but that suggest they are internally doing a fallback to
> JSF 1.2
> state saving over that portion, which is just the same thing as we do with
> org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS_PRESERVE_STATE. This has
> obviously a cost over the state size.
>
> The problem is a fallback to JSF 1.2 is not a satisfactory solution.
> We have already
> heard about that. State gets bigger, slow performance, because the
> implementation
> needs to make complex calculations over the state, components that loses
> the
> state, unexpected exceptions of all kinds, that nobody knows how to
> reproduce it
> and how to fix them. It could work in the simple cases, but if you
> start to add more
> and more things to the page, sooner or later the page will break.
>
> If you don't believe me take a look at this error:
>
> https://java.net/jira/browse/JAVASERVERFACES-3241
>
> With that report only, not even the wizard Mandrake can know what the
> hell happened.
> This is not a rant. This is "old history", something that we already
> realized many years
> ago. See this mail:
>
>
> https://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2011-10/message/2
>
> Now what we know what is wrong with c:forEach and that there are
> workarounds for
> this stuff, we can ask the big question:
>
> Why the same code fails in MyFaces?
>
> It fails because MyFaces applies PSS algorithm the way it supposed to be by
> JSF spec. When you add a new tab, the component is marked to be restored
> fully, so the first time it doesn't happen anything, but the new tab is
> saved fully into the state, but when the algorithm restores the view, it
> tries to calculate the initial state and then it add the added tab, but
> it was added twice because the initial state changed, and one of the base
> principles is that the initial state does not change across requests. The
> underlying problem is c:forEach should store the model of the first request
> processed so PSS algorithm can still be valid. But that can only be done if
> the model is Serializable.
>
> So, theoretically the default mode of PSS algorithm should implement the
> solution proposed and already added in MyFaces 2.2.2. From the beginning
> we have known that PSS algorithm is not 100% fail safe, but we also know
> the only remaining case is this one.
>
> Can we fix this case? even if it is not the best solution?
>
> What we can do is add some lines of code that checks for the duplicate when
> the component is added and use the saved version of the component instead.
> More than a fix is a "damage control" alternative. But it sounds good,
> because the check can be done quickly in the nearby, where the component
> is supposed to be, and you can activate the case, just checking if the
> component is managed by facelets.
>
> I'll try the fix and if works, I'll commit it on all branches (2.0.x, 2.1.x
> and 2.2.x).
>
> regards,
>
> Leonardo
>
> 2014-04-11 14:06 GMT+02:00 Leonardo Uribe <lu...@gmail.com>:
> > Hi
> >
> > It seems the example works under this configurations on 2.1.x branch:
> >
> > 1. Set javax.faces.PARTIAL_STATE_SAVING to false.
> >
> > or
> >
> > 2. Add the page in javax.faces.FULL_STATE_SAVING_VIEW_IDS (disable PSS
> > on the related page only).
> >
> > or
> >
> > 3. Set org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS_PRESERVE_STATE
> to true.
> >
> > The reason is simple, in all previous cases what we are doing is mark
> > that part of the tree to be saved fully. I'll give a detailed
> > explanation later, I just wanted to give the workarounds for the time
> > being.
> >
> > regards,
> >
> > Leonardo Uribe
> >
> >
> > 2014-04-10 19:21 GMT+02:00 Leonardo Uribe <lu...@gmail.com>:
> >> Hi
> >>
> >> I was able to reproduce the problem. But I can confirm the problem is
> >> gone once you use 2.2.3 and make DynamicTab implements Serializable.
> >> I'll try to find if there is a way to fix it somehow in 2.1.x, so it
> >> can be "less broken".
> >>
> >> regards,
> >>
> >> Leonardo
> >>
> >> 2014-04-10 17:05 GMT+02:00 Volker Weber <v....@inexso.de>:
> >>> Hi,
> >>>
> >>> we are facing a duplicate id exception after adding a component in
> >>> applicationPhase to to view.
> >>>
> >>> We are using c:forEach and add an item to the iterated list in the
> >>> actionListener.
> >>>
> >>> Saving this view is done without problem, but restoring this saved view
> >>> results in a view containing the new component twice! Which results in
> a
> >>> duplicate id exception when saving this restored view again.
> >>>
> >>> I found that this new component on creation is marked as
> >>> "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW.
> >>> On restore view the facelet is used to recreate the view, which is
> >>> including the new component because it is now included in the forEach
> >>> iteration.
> >>> After recreating the view with the facelet the view is extended with
> the
> >>> components which were marked with "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW
> >>> when storing the view.
> >>>
> >>> At this point the component is duplicated in the view.
> >>>
> >>> The Problem is in
> >>> org.apache.myfaces.view.facelets.DefaultFaceletsStateManagementStrategy
> >>> (row 437 to 451 in 2.1.10 sources), the component is added to the view
> >>> regardless if there is already a component with same id.
> >>>
> >>> I have a example app to reproduce this Problem can downloaded here
> >>> http://www.inexso.net/files/myfaces-dynamic-test.zip .
> >>>
> >>> Run this with "mvn jetty:run" to reproduce this.
> >>>
> >>> Running this with "mvn -Dcontainer=jetty-mojarra jetty:run" did not
> give
> >>> me any error.
> >>>
> >>>
> >>> Regards,
> >>>   Volker
> >>>
> >>>
> >>> --
> >>> Volker Weber
> >>> -Leiter Software-Entwicklung-
> >>>
> >>> inexso - information exchange solutions GmbH
> >>> Ofener Str. 30      | 26121 Oldenburg
> >>> www.inexso.de
> >>>
> >>> Firmensitz: Oldenburg | Amtsgericht Oldenburg HRB 205251
> >>> Geschäftsführer: Stefan Schulte, Michael Terschüren
> >>>
> >>>
>



-- 
inexso - information exchange solutions GmbH
Ofener Straße 30 | 26121 Oldenburg
www.inexso.de

Re: RestoreView Problem after dynamically added component

Posted by Leonardo Uribe <lu...@gmail.com>.
Hi

The problem of c:forEach is a long story. I'll try to make a summary. This tag
originally came from JSP, and then it was somewhat "copied" into facelets,
together with other tags. But the copy in facelets had a problem: it doesn't
store the "model" internally (JSP variant does). Instead, the model is
reapplied each time a refresh over the page is done, usually before render
response phase by facelets. The effect of the refresh is usually remove or
add the necessary components to synchronize the model. Since in JSF 1.2 the
whole component tree is saved in the state, nobody noticed anything. Well,
some guys saw the problem. See:

http://drewdev.blogspot.co.at/2008/08/cforeach-with-jsf-could-ruin-your-day.html

Andrew Robinson did a partial solution in tr:forEach, but I'm afraid the
only solution that will work reliable is fix c:forEach.

But with the introduction of JSF 2 Partial State Saving (PSS) algorithm, it
was changed the way how the component tree was saved and restored. This
introduced some new problems:

1. The generated ids were not stable enough in JSF 1.2, and PSS requires
stable ids, which in other words means the same ids are generated to the
component each time facelets algorithm is applied. Content generated by
c:forEach is not stable enough, so you have situations where the state
just get lost.

2. Now the component state is split in two: the initial state and the delta
state, but c:forEach is a facelet tag that is not bound to a component
counterpart, so it doesn't have an associated state to be split.

3. In JSF 1.2 it was only one refresh point (before render response), but
JSF 2 PSS added one extra point where facelets algorithm is executed
(restore view phase). Please note if PSS is disabled, this is not called.

The final result is we have not just one problem but a set of different
problems all over the same facelet tag. We finally solved the problem in
MyFaces 2.2.2 after a lot of struggle. The code could be backport into 2.1.x,
but at this stage it was considered too risky, because it can introduce
instability to existing applications. We can consider the backport but only
after some more releases of 2.2.x branch.

There is a set of well known workarounds, so you can just apply one of them
and problem solved. It is too hard to explain in detail every change done in
the algorithm, it took a lot of time and effort, but the important thing to
remind is the new code in 2.2.2 copies the model as in JSP to create the
initial state. So, the model needs to be Serializable and if it is not, it
fallback to the old algorithm, so that's why the code works after you make
DynamicTab implements Serializable interface. That's the recommended
solution.

Why it works in Mojarra? it doesn't work, it apparently work, but it doesn't.
Try this change:

  <h:panelGroup>
  <h:inputText value="begin"/>
  <c:forEach items="#{tabProvider.tabs}" var="tab">
    <h:inputText value="#{tab.outValue}"/>
    <br/>
  </c:forEach>
  <h:inputText value="end"/>
  </h:panelGroup>

The generated markup is this (Mojarra 2.1.28):

<input type="text" name="j_idt6" value="begin" />
    <input type="text" name="j_idt7" value="Content of dynamic Tab number 0" />
    <br />
    <input type="text" name="j_idt9" value="Content of dynamic Tab number 1" />
    <br />
    <input type="text" name="j_idt11" value="Content of dynamic Tab number 2" />
    <br />
<input type="text" name="j_idt13" value="end" />

Can you see the problem? The id generation is completely broken! Why?
because you can put a c:if before the c:forEach and change the way how the ids
are generated, and in that way alter the state and have some nasty exception
like duplicate ids or "state get lost" problems. That's a "Chronicle of a
Death Foretold". If you make a click on the button, you'll see this:

<input type="text" name="j_idt6" value="begin" />
    <input type="text" name="j_idt7" value="Content of dynamic Tab number 0" />
    <br />
    <input type="text" name="j_idt9" value="Content of dynamic Tab number 1" />
    <br />
    <input type="text" name="j_idt11" value="Content of dynamic Tab number 2" />
    <br />
    <input type="text" name="j_idt15" value="Content of dynamic Tab number 3" />
    <br />
<input type="text" name="j_idt13" value="end" />

Yes, it works, but that suggest they are internally doing a fallback to JSF 1.2
state saving over that portion, which is just the same thing as we do with
org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS_PRESERVE_STATE. This has
obviously a cost over the state size.

The problem is a fallback to JSF 1.2 is not a satisfactory solution.
We have already
heard about that. State gets bigger, slow performance, because the
implementation
needs to make complex calculations over the state, components that loses the
state, unexpected exceptions of all kinds, that nobody knows how to reproduce it
and how to fix them. It could work in the simple cases, but if you
start to add more
and more things to the page, sooner or later the page will break.

If you don't believe me take a look at this error:

https://java.net/jira/browse/JAVASERVERFACES-3241

With that report only, not even the wizard Mandrake can know what the
hell happened.
This is not a rant. This is "old history", something that we already
realized many years
ago. See this mail:

https://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2011-10/message/2

Now what we know what is wrong with c:forEach and that there are workarounds for
this stuff, we can ask the big question:

Why the same code fails in MyFaces?

It fails because MyFaces applies PSS algorithm the way it supposed to be by
JSF spec. When you add a new tab, the component is marked to be restored
fully, so the first time it doesn't happen anything, but the new tab is
saved fully into the state, but when the algorithm restores the view, it
tries to calculate the initial state and then it add the added tab, but
it was added twice because the initial state changed, and one of the base
principles is that the initial state does not change across requests. The
underlying problem is c:forEach should store the model of the first request
processed so PSS algorithm can still be valid. But that can only be done if
the model is Serializable.

So, theoretically the default mode of PSS algorithm should implement the
solution proposed and already added in MyFaces 2.2.2. From the beginning
we have known that PSS algorithm is not 100% fail safe, but we also know
the only remaining case is this one.

Can we fix this case? even if it is not the best solution?

What we can do is add some lines of code that checks for the duplicate when
the component is added and use the saved version of the component instead.
More than a fix is a "damage control" alternative. But it sounds good,
because the check can be done quickly in the nearby, where the component
is supposed to be, and you can activate the case, just checking if the
component is managed by facelets.

I'll try the fix and if works, I'll commit it on all branches (2.0.x, 2.1.x
and 2.2.x).

regards,

Leonardo

2014-04-11 14:06 GMT+02:00 Leonardo Uribe <lu...@gmail.com>:
> Hi
>
> It seems the example works under this configurations on 2.1.x branch:
>
> 1. Set javax.faces.PARTIAL_STATE_SAVING to false.
>
> or
>
> 2. Add the page in javax.faces.FULL_STATE_SAVING_VIEW_IDS (disable PSS
> on the related page only).
>
> or
>
> 3. Set org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS_PRESERVE_STATE to true.
>
> The reason is simple, in all previous cases what we are doing is mark
> that part of the tree to be saved fully. I'll give a detailed
> explanation later, I just wanted to give the workarounds for the time
> being.
>
> regards,
>
> Leonardo Uribe
>
>
> 2014-04-10 19:21 GMT+02:00 Leonardo Uribe <lu...@gmail.com>:
>> Hi
>>
>> I was able to reproduce the problem. But I can confirm the problem is
>> gone once you use 2.2.3 and make DynamicTab implements Serializable.
>> I'll try to find if there is a way to fix it somehow in 2.1.x, so it
>> can be "less broken".
>>
>> regards,
>>
>> Leonardo
>>
>> 2014-04-10 17:05 GMT+02:00 Volker Weber <v....@inexso.de>:
>>> Hi,
>>>
>>> we are facing a duplicate id exception after adding a component in
>>> applicationPhase to to view.
>>>
>>> We are using c:forEach and add an item to the iterated list in the
>>> actionListener.
>>>
>>> Saving this view is done without problem, but restoring this saved view
>>> results in a view containing the new component twice! Which results in a
>>> duplicate id exception when saving this restored view again.
>>>
>>> I found that this new component on creation is marked as
>>> "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW.
>>> On restore view the facelet is used to recreate the view, which is
>>> including the new component because it is now included in the forEach
>>> iteration.
>>> After recreating the view with the facelet the view is extended with the
>>> components which were marked with "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW
>>> when storing the view.
>>>
>>> At this point the component is duplicated in the view.
>>>
>>> The Problem is in
>>> org.apache.myfaces.view.facelets.DefaultFaceletsStateManagementStrategy
>>> (row 437 to 451 in 2.1.10 sources), the component is added to the view
>>> regardless if there is already a component with same id.
>>>
>>> I have a example app to reproduce this Problem can downloaded here
>>> http://www.inexso.net/files/myfaces-dynamic-test.zip .
>>>
>>> Run this with "mvn jetty:run" to reproduce this.
>>>
>>> Running this with "mvn -Dcontainer=jetty-mojarra jetty:run" did not give
>>> me any error.
>>>
>>>
>>> Regards,
>>>   Volker
>>>
>>>
>>> --
>>> Volker Weber
>>> -Leiter Software-Entwicklung-
>>>
>>> inexso - information exchange solutions GmbH
>>> Ofener Str. 30      | 26121 Oldenburg
>>> www.inexso.de
>>>
>>> Firmensitz: Oldenburg | Amtsgericht Oldenburg HRB 205251
>>> Geschäftsführer: Stefan Schulte, Michael Terschüren
>>>
>>>

Re: RestoreView Problem after dynamically added component

Posted by Leonardo Uribe <lu...@gmail.com>.
Hi

It seems the example works under this configurations on 2.1.x branch:

1. Set javax.faces.PARTIAL_STATE_SAVING to false.

or

2. Add the page in javax.faces.FULL_STATE_SAVING_VIEW_IDS (disable PSS
on the related page only).

or

3. Set org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS_PRESERVE_STATE to true.

The reason is simple, in all previous cases what we are doing is mark
that part of the tree to be saved fully. I'll give a detailed
explanation later, I just wanted to give the workarounds for the time
being.

regards,

Leonardo Uribe


2014-04-10 19:21 GMT+02:00 Leonardo Uribe <lu...@gmail.com>:
> Hi
>
> I was able to reproduce the problem. But I can confirm the problem is
> gone once you use 2.2.3 and make DynamicTab implements Serializable.
> I'll try to find if there is a way to fix it somehow in 2.1.x, so it
> can be "less broken".
>
> regards,
>
> Leonardo
>
> 2014-04-10 17:05 GMT+02:00 Volker Weber <v....@inexso.de>:
>> Hi,
>>
>> we are facing a duplicate id exception after adding a component in
>> applicationPhase to to view.
>>
>> We are using c:forEach and add an item to the iterated list in the
>> actionListener.
>>
>> Saving this view is done without problem, but restoring this saved view
>> results in a view containing the new component twice! Which results in a
>> duplicate id exception when saving this restored view again.
>>
>> I found that this new component on creation is marked as
>> "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW.
>> On restore view the facelet is used to recreate the view, which is
>> including the new component because it is now included in the forEach
>> iteration.
>> After recreating the view with the facelet the view is extended with the
>> components which were marked with "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW
>> when storing the view.
>>
>> At this point the component is duplicated in the view.
>>
>> The Problem is in
>> org.apache.myfaces.view.facelets.DefaultFaceletsStateManagementStrategy
>> (row 437 to 451 in 2.1.10 sources), the component is added to the view
>> regardless if there is already a component with same id.
>>
>> I have a example app to reproduce this Problem can downloaded here
>> http://www.inexso.net/files/myfaces-dynamic-test.zip .
>>
>> Run this with "mvn jetty:run" to reproduce this.
>>
>> Running this with "mvn -Dcontainer=jetty-mojarra jetty:run" did not give
>> me any error.
>>
>>
>> Regards,
>>   Volker
>>
>>
>> --
>> Volker Weber
>> -Leiter Software-Entwicklung-
>>
>> inexso - information exchange solutions GmbH
>> Ofener Str. 30      | 26121 Oldenburg
>> www.inexso.de
>>
>> Firmensitz: Oldenburg | Amtsgericht Oldenburg HRB 205251
>> Geschäftsführer: Stefan Schulte, Michael Terschüren
>>
>>

Re: RestoreView Problem after dynamically added component

Posted by Leonardo Uribe <lu...@gmail.com>.
Hi

I was able to reproduce the problem. But I can confirm the problem is
gone once you use 2.2.3 and make DynamicTab implements Serializable.
I'll try to find if there is a way to fix it somehow in 2.1.x, so it
can be "less broken".

regards,

Leonardo

2014-04-10 17:05 GMT+02:00 Volker Weber <v....@inexso.de>:
> Hi,
>
> we are facing a duplicate id exception after adding a component in
> applicationPhase to to view.
>
> We are using c:forEach and add an item to the iterated list in the
> actionListener.
>
> Saving this view is done without problem, but restoring this saved view
> results in a view containing the new component twice! Which results in a
> duplicate id exception when saving this restored view again.
>
> I found that this new component on creation is marked as
> "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW.
> On restore view the facelet is used to recreate the view, which is
> including the new component because it is now included in the forEach
> iteration.
> After recreating the view with the facelet the view is extended with the
> components which were marked with "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW
> when storing the view.
>
> At this point the component is duplicated in the view.
>
> The Problem is in
> org.apache.myfaces.view.facelets.DefaultFaceletsStateManagementStrategy
> (row 437 to 451 in 2.1.10 sources), the component is added to the view
> regardless if there is already a component with same id.
>
> I have a example app to reproduce this Problem can downloaded here
> http://www.inexso.net/files/myfaces-dynamic-test.zip .
>
> Run this with "mvn jetty:run" to reproduce this.
>
> Running this with "mvn -Dcontainer=jetty-mojarra jetty:run" did not give
> me any error.
>
>
> Regards,
>   Volker
>
>
> --
> Volker Weber
> -Leiter Software-Entwicklung-
>
> inexso - information exchange solutions GmbH
> Ofener Str. 30      | 26121 Oldenburg
> www.inexso.de
>
> Firmensitz: Oldenburg | Amtsgericht Oldenburg HRB 205251
> Geschäftsführer: Stefan Schulte, Michael Terschüren
>
>