myfaces-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Leonardo Uribe <lu4...@gmail.com>
Subject Re: RestoreView Problem after dynamically added component
Date Mon, 14 Apr 2014 16:29:29 GMT
Hi

2014-04-14 10:20 GMT+02:00 Volker Weber <v.weber@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 <lu4242@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 <lu4242@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 <lu4242@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.weber@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

Mime
View raw message