Issue Details (XML | Word | Printable)

Key: JAVASERVERFACES_SPEC_PUBLIC-790
Type: Bug Bug
Status: Open Open
Priority: Critical Critical
Assignee: Unassigned
Reporter: werpu12
Votes: 48
Watchers: 35
Operations

If you were logged in you would be able to see more operations.
javaserverfaces-spec-public

javax.faces.ViewState + PPR does not work out for cross form ppr cases

Created: 06/May/10 12:38 AM   Updated: 13/Jan/14 03:36 PM
Component/s: Ajax/JavaScript
Affects Version/s: 2.0
Fix Version/s: 2.3

Time Tracking:
Original Estimate: 5 days
Original Estimate - 5 days
Remaining Estimate: 5 days
Remaining Estimate - 5 days
Time Spent: Not Specified
Time Spent - Not Specified

File Attachments: 1. Text File 790-js-workaround.txt (2 kB) 06/Dec/11 05:06 PM - mdirkse
2. Text File changebundle.txt (7 kB) 23/Sep/11 12:49 PM - frederickkaempfer
3. Text File changebundle.txt (6 kB) 22/Sep/11 02:34 PM - frederickkaempfer
4. Java Source File ExtendedViewHandler.java (5 kB) 29/Dec/11 03:25 PM - sharath.naik

Environment:

Operating System: All
Platform: Macintosh

Issue Links:
Related
 

Issuezilla Id: 790
Status Whiteboard:

size_small importance_small

Tags:
Participants: arjan tijms, balusc, boogi, codylerum, Ed Burns, frederickkaempfer, javaone9, kfyten, mdirkse, rogerk, sharath.naik, swathireddy12, tedgoddard, werpu and werpu12


 Description  « Hide

Following problem
<h:form id="a">
<h:commandButton action="#{TestBean.action}" value="submit"/>
</h:form>

<h:form id="b">
<h:commandLink value="ajax ReRender"
onclick="jsf.ajax.request(this,event,{execute:'b a',render:'a'}); return false;"/>
</h:form>

Cannot work out because, the viewstate is returned as separate viewstate block
(in both implementations the <update id="a"> does not pass the viewState in the
update block.

Now the specification says:
If an update element is found in the response with the identifier
javax.faces.ViewState:

<update id="javax.faces.ViewState">
<![CDATA[...]]>
</update>

locate and update the submitting form's javax.faces.ViewState value with the
CDATA contents from the response.


Which means in this special case that the viewState for form a is lost.
Mojarra has fixed this to some degree by setting the viewstate if a direct form
render on a happens.
However if you do following:
<h:panelGroup id="myGroup">
<h:form id="a">
<h:commandButton action="#{TestBean.action}" value="submit"/>
</h:form>
</h:panelGroup>

<h:form id="b">
<h:commandLink value="ajax ReRender"
onclick="jsf.ajax.request(this,event,{execute:'b a',render:'myGroup'}); return
false;"/>
</h:form>

Mojarra also fails.

The problem here lies clearly with the spec, I am not sure why the viewstate is
only updated to the issuing form.

Either all forms must be updated or at least the forms which are processed both
within the execute and render parts.

I also opened a discussion on the open mailinglist regarding this, since this is
a usecase which can happen quite often in a typical rich client szenario where a
lot of detachments can happen to satisfy ie6 and multiple forms are the norm if
you have floating frames.



Ed Burns added a comment - 24/May/10 11:00 AM

Agree for inclusion in 2.0 Rev a


Ed Burns added a comment - 24/May/10 12:55 PM

take ownership.


Ed Burns added a comment - 27/May/10 11:52 AM

I agree we should update all forms. Move to 2.1.


Ed Burns added a comment - 08/Jun/10 01:05 PM

triage


Ed Burns added a comment - 24/Jun/10 01:32 PM

Change target milestone.


werpu12 added a comment - 12/Jul/10 03:09 AM

Actually I personally think the only possible solution here is to offload this
to the server, instead of issuing <update id="javax.faces.ViewState"> we have to
extend the protocol here so that the client is notified which form and viewstate
element needs to be updated.
I am not sure if updating all forms automatically really resolves the issue,
because in a portlet scenario this does not work out, how about client side
state saving or even worse if someone introduces multiple viewroots
programmatically just as portlets do?


rogerk added a comment - 27/Oct/10 02:16 PM

triage


frederickkaempfer added a comment - 21/Sep/11 03:52 PM

The same problem occurs (in Mojarra 2.1.3) when the entire ViewRoot is replaced via ajax. In that case none of the forms get their view state updated. You need to submit a form at least twice in order to trigger the execution of the full JSF lifecycle. The first time only a new ViewState field is created in the submitted form.

At least all forms contained in the rendered section need to have their view state field updated. In the current implementation a ViewState field is only created if the render target is it self a form (not a parent of forms).

Updating all forms on the page is also a possibility though strictly speaking forms not contained in the <update/> section of the Ajax request are not part of the newly generated view state.

Possible duplicates of this bug I found so far:
http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-1024
http://java.net/jira/browse/JAVASERVERFACES-2199


frederickkaempfer added a comment - 22/Sep/11 02:34 PM

I have created a fix for the issue, which updates the ViewState field for forms which are children of the specified render targets.

Furthermore it handles the case where render="@all" is used. In this case all forms in the document will have their ViewState field updated. Previously this caused forms to loose their ViewState.

Note that this fix does not update all forms in the document, but only those which have been re-rendered (thus are part of the newly generated view on the server).


werpu12 added a comment - 22/Sep/11 02:48 PM

Actually I have similar fixes for myfaces, I basically update all forms which have render targets in or forms which are part of a render target.
Additionally to that I added a update all config option which is outside of the spec.
But nevertheless.

But that is just a hack in my opinion. The root problem is in the protocol.
The protocol simply needs to deliver a list of form ids which need a viewstate update.
So I personally think the only viable long term solution to this problem simply is to update the protocol of jsf 2.2 in this regar ala <update id="javax.faces.ViewState" updateIds="id1 id2 id3">dkghsfkjgh</update> or for backwards compatibility reasons introduce an entirely new tag like <updateViewState id="...">....


rogerk added a comment - 22/Sep/11 03:21 PM

Your workarounds are fine for now, but clearly this needs to be a spec change for the better.


frederickkaempfer added a comment - 22/Sep/11 04:12 PM

If it was safe to assume that the ViewState does not change during a partial request the logic can be simplified by updating ALL forms in the document (not just render targets).

In Mojarra's ServerSideStateHelper (line 200) this assumption holds true, but I did not find anything in the spec enforcing this.

Additionally this would also take care of any Form dynamically added to the view or any render ids added via facesContext.getPartialViewContext().getRenderIds().add(...);

Right now only the "javax.faces.partial.render" argument is consulted which is indeed suboptimal.

I can change the workaround to simply update all forms in the page, which would also make it less of a "Hack". What do you think?


rogerk added a comment - 22/Sep/11 04:26 PM

I was thinking along those same lines..
In which case we would not need to introduce a new response element as Werner mentioned.
It's been sometime since we looked at this - it has been discussed before.
Are there any portlet implications (namespace related) by doing this (that was brought up - but could be for a different issue)..


werpu12 added a comment - 22/Sep/11 06:38 PM

Actually there are portlet implications, hence i stayed away from updating all forms automatically, but made it a config option you can turn on in myfaces.

The implication is that different portlets can host different forms under different viewroots and hence have different viewstates.
Now the main problem is, we dont have any viewroot information. So it comes down to following:
a) either send the viewstate info with an altered protocol i mentioned

  • safe since every form must have a unique id in the dom no double ids are allowed

b) add viewroot information and then update all forms under the corresponding viewroot - should be safe as well but is in my opinion not as clean as option a) and probably causes an alteration of the protocol as well (ala introduction of a viewroot element) or on the api side. I am not sure if we have a scenario where we have different viewstates under one viewroot though. I never really bothered to look deeper into this aspect of the jsf spec.

c) Try to move forward with the usual hacks - worst option of all

In my personal experience this multi form issue has been a huge issue for the users, I had about 5-10 requests on the myfaces mailinglist where users had problems and thought it was an issue of the implementation.


frederickkaempfer added a comment - 23/Sep/11 09:08 AM

It's true that updating all forms won't work in any case where there are multiple viewroots involved. Anyhow I was hoping to find a solution that is usable even with the 2.1 and 2.0 spec versions, because this bug is not an enhancement, but currently breaks a couple of basic Ajax features in JSF. To summarize them quickly:

1. Specifying parents of forms as render targets.
2. Using the @all render target on any page with multiple forms.
3. Replacing the entire viewroot, for example via <h:commandLink action="myNavigationOutcome"><f:ajax/></h:commandLink>
4. Dynamically adding RenderIds that include forms in JSF Lifecycle with facesContext.getPartialViewContext().getRenderIds().add(...)

The workaround I provided should at least make 1-3 work again. Updating all forms in the document would make 1-4 work, but as you said will break Portlet compatibility.

There is another option to consider that does not require a protocol change:

Before the doUpdate function is called for any of the changes read the view state information at the end of the partial response document and save it in a variable in the jsf.ajax.response function. the viewstate can then be passed to any call of doUpdate function, which can then take care of creating view state fields where it is appropriate. This way we would not have to redundantly send the render ids, as they already provided as the <update> ids.


frederickkaempfer added a comment - 23/Sep/11 12:49 PM

Here is a changebundle that updates the ViewState for each form element that is replaced during the ajax update.

I wonder if a Spec change is even necessary because: If a form is processed during an update it means that its ViewState field has to be updated (or created) as well. With this patch it is now done in the doUpdate function. On the other hand, if a form is not re-rendered there is no compelling reason to update its ViewState field, because if it had been changed on the server-side this would not be visible in the browser.

So following this logic, specifying the "updateIds" in a new protocol element will always replicate the render ids which are already contained in the <update> tags (or any of the forms contained in them, which can also be found using JavaScript, like it is done in the provided patch).

Do you think this is still too "hack"-ish?


werpu12 added a comment - 23/Sep/11 01:03 PM

Unfortunately things are not that simple. I cannot speak for mojarra, but I assume it is similar, but myfaces has a meta information in the viewstate which also pinpoints to the viewid (and the state history as well) so if you do not update a second form even if you dont have a rerender there, you basically at a submit from that form can get a cannot find viewid once it drops out of the view history.

So we have two problems
a) update all forms automatically is prevented due to the fact that it breaks in a portlet environment

b) not updating not rendered forms might break the state history information of the viewstate not updated

So a pure javascript fixup will work and might work under normal non portlet circumstances (the simplest one probably is simply to update all forms for a normal webapp) but it will break in corner cases like portlet environments. Thats one of the reasons why I implemted about three fallback modes for this problem in myfaces. So that if even one fallback fails the user can switch to another one which might suite to his environment.


frederickkaempfer added a comment - 23/Sep/11 02:48 PM

I think Mojarra does not even change the ViewState during partial requests.

Nevertheless I suppose there are two separate issues surfacing here that are remotely related. This patch should work in a Portlet scenario as well as the unpatched version and it fixes the 4 issues I mentioned earlier including the initial bug report. Contrary to updating all forms in the document, it doesn't make it any worse for the portlet scenario. But yes, it is a preliminary hack, if you consider the following change as the right way to handle the situation:

In your last comment you basically said that on each Ajax request all forms contained in a one ViewRoot have to be updated with new state information - at least in the case of MyFaces (it would work for Mojarra as well because the ViewState doesn't change):

  • If we are in a "normal" (non-portlet) environment with just one ViewRoot, this means updating all forms contained in the entire document.
  • If we are in a portlet environment it would mean updating all forms that are contained in the current ViewRoot.

So wouldn't the best course of action be your option b): make it possible to specify the view root element's id additionally in the partial response. If it is not specified or is set to a default value, update the entire document, if not only update forms contained in the viewroot element. This would also add the possibility to rerender a complete portlet with the special render target @all, which represents entire ViewRoot element. Sadly I don't know enough about the portlet spec to tell if this is a good idea and if this change is enough to support it properly.

Providing a list of update ids would then again be redundant, because the forms that need updating depend only on the ViewRoot that is currently - partly or in whole - redisplayed.

If this sounds like the right way to do it, then applying the changebundle will be counterproductive, because it is unnecessarily complex compared to updating all forms in the current ViewRoot. On the other hand it could even be backported to 2.0 or 2.1, it does not make things worse for portlets and it fixes the 4 critical bugs/problems i mentioned.


Ed Burns added a comment - 23/Sep/11 03:33 PM

Thank you for excellent and clear analysis. Given my desire to include sketches for JAVASERVERFACES_SPEC_PUBLIC-730, JAVASERVERFACES_SPEC_PUBLIC-517, and JAVASERVERFACES_SPEC_PUBLIC-802, in next week's EDR of the spec, I'm going to defer this til after JavaOne.


codylerum added a comment - 02/Nov/11 08:56 PM

This also is an issue with popup panels which often have their own form that when submitted rerenders content on the main page.

Since any forms on the main page did not participate in the submit they don't rerender with a viewstate and are unusable.


mdirkse added a comment - 06/Dec/11 05:06 PM

Added a javascript workaround that fixes this issue and can be executed via the browser onLoad event handler. There are 2 versions: a plain JS one, and a jQuerified one. Tested and confirmed for Mojarra 2.1.2 (and jQuery 1.7.x).
Mad propz to frederickkaempfer for the original JS code.


werpu12 added a comment - 06/Dec/11 05:13 PM - edited

Sorry to crush some hopes here, but the posted solution is exactly the one I have in myfaces for the non portlet case (you can turn it on via a config param). And there it works, but it can only work in a non portlet environment, because there you have only one viewroot. So you can update all forms under document.

In a portlet environment you can have several viewroots under document with independend viewstates and independend forms. There the patch ultimately will break your portlet environment by applying wrong viewroots to other portlets. Thats because the which dom node is the viewroot info is simply missing on the client side and/or the protocol.

As I said before unless you have the viewroot info or you know which forms you update there is no way to resolve that issue for the portlet and non portlet case at the same time. The problem really is a problem of the protocol not the implementation.


rogerk added a comment - 06/Dec/11 05:26 PM

I agree with Wevner's earlier assessment - that to handle the portlet case - multiple independent view root (eaxh with one or more forms) - we do need a new "server to client" message protocol that draws the association of which forms the view state should apply.


mdirkse added a comment - 06/Dec/11 05:36 PM

werpu12 is right, the workaround I posted only works for the non-portlet case (and is only verified for mojarra 2.1.2)


sharath.naik added a comment - 29/Dec/11 03:25 PM

Updating the MultiViewHandler's [ public void writeState(FacesContext context) throws IOException ] seems to fix the issue. The change made is to add the view state hidden field for forms, even when is a ajax request.

What is the purpose of not writing the view state if it is a partial request in this method?

Attaching the custom ViewHandler to override this method. would this be a fix that can be considered to be moved to MultiViewHandler?


frederickkaempfer added a comment - 02/Jan/12 10:38 AM - edited

@ sharath.naik: It's probably left out because the jsf ajax response already includes the view state information in an <update> tag, so the field can be created using JavaScript.

As I said earlier, the minimum a JSF implementation should do is update the view state information for all forms included in the render target list. I don't see how this creates any new problem in the portlet environment and that is what the latest changebundle I submitted does. Currently the algorithm detailed in the spec is not general enough and should be corrected.

A broader approach is to update all forms under the current view root. For that a protocol extension is necessary which tells the client which DOM element represents the view root. It would also enable scenarios where the whole view root is replaced (like @all and navigation) for the portlet environment. In my opinion for this a new spec enhancement should be issued.


codylerum added a comment - 15/Feb/12 05:00 PM

Is the attached workaround meant to be called from the oncomplete of an ajax component or am I missing another way to trigger it?


frederickkaempfer added a comment - 22/Feb/12 12:50 PM

@codylerum: it should be enough to execute the script once on a page load, after jsf.js is loaded of course.


Ed Burns added a comment - 03/May/12 06:35 PM

TG> (First of all, javax.faces.ViewState should not be modified for Ajax
TG> updates using server-side state-saving, but this simple approach
TG> does not appear to be the current direction.)

Can you please elaborate more on what you mean by this? The issue
doesn't mention anything about Ajax specifically. Is this a separate
concern you are raising, Ted?

TG> We are a bit unclear on how the current implementation is intended
TG> to work, but expect that something like the following is necessary:

TG> When the request is submitted, call
TG> getElementsByName('javax.faces.ViewState') and store the list of IDs
TG> for all with equal value to that of the javax.faces.ViewState in the
TG> submitting form. In the case of future JSP inclusion or current
TG> Portlets, there will be javax.faces.ViewState fields not associated
TG> with the current view. They will have different values and are not
TG> included in the list.

TG> When the partial response is generated, it may contain rendered
TG> hidden inputs with name javax.faces.ViewState and unique IDs, but
TG> also contains the special case <update
TG> id="javax.faces.ViewState"><![CDATA[...]]></update>. The previously
TG> stored list of IDs are now all updated to use the new value. Any
TG> javax.faces.ViewState hidden fields that have been modified by the
TG> page update itself (potentially now having different IDs) have the
TG> correct value anyway because they were just updated.

These two comments are better suited to JAVASERVERFACES_SPEC_PUBLIC-790,
so I'll copy them there. Ted, can you please take a look at 790 and see
if you agree that your comments pertain more to that issue than this
one?


tedgoddard added a comment - 07/May/12 05:11 PM

At least in the case of MyFaces, this complicates things considerably:

"Unfortunately things are not that simple. I cannot speak for mojarra, but I assume it is similar, but myfaces has a meta information in the viewstate which also pinpoints to the viewid (and the state history as well) so if you do not update a second form even if you dont have a rerender there, you basically at a submit from that form can get a cannot find viewid once it drops out of the view history."

It makes it necessary to update the ViewState key for every request. I was under the impression that a similar strategy was being considered for Mojarra.

In my above comments, I assume that the form Renderer would be modified to always write out the ViewState (even for partial rendering). Alternatively, the javax.faces.ViewState fields could be updated via two cases:

  • javax.faces.ViewState found in the current HTML document that match the submitting javax.faces.ViewState will be updated after the current response
  • javax.faces.ViewState found in the updated regions will be updated after the current response

This should handle portlet, servlet inclusion, and multiple form cases.


boogi added a comment - 31/May/12 12:25 PM

Hi,
It says on fix version "2.2 Sprint 12" but it appears as unresolved on that sprint.
Do you a new estimation for the solution of this issue?


javaone9 added a comment - 02/Nov/12 01:20 AM

I tried 2.1.14. it did not work.
I think this issue is urgent for any applications.


frederickkaempfer added a comment - 18/Apr/13 07:24 AM - edited

Please don't forget this issue for 2.2. It's one of the most annoying and frustrating aspects of the JSF Ajax experience. It would be a shame if the JSF community would have to wait another spec release -possibly another year- before this gets fixed.

tedgoddard made it very clear how to proceed with this issue in the previous comment.

Thanks a lot.


kfyten added a comment - 25/Apr/13 10:13 PM

Roger/Ed - Just wanted to make it clear that this issue is currently blocking ICEfaces support for JSF 2.2. From a roadmap perspective it would be very helpful to us if this JIRA could be updated to reflect the current reality in terms of whether/when this issue may be resolved prior to 2.2 final release.

Thx.


rogerk added a comment - 26/Apr/13 02:40 PM

Unfortunately I don't believe this made it into 2.2.


balusc added a comment - 08/May/13 11:30 AM

This is really unfortunate.

In the meanwhile, developers can use this script to workaround the issue: http://balusc.blogspot.com/2011/09/communication-in-jsf-20.html#AutomaticallyFixMissingJSFViewStateAfterAjaxRendering


frederickkaempfer added a comment - 08/May/13 11:53 AM

@balusc: I think that the workaround scripts have to be changed for 2.2, because the id of the ViewState field has been changed slightly. I didn't yet have time to look into it.


arjan tijms added a comment - 08/May/13 12:06 PM

I think that the workaround scripts have to be changed for 2.2, because the id of the ViewState field has been changed slightly. I didn't yet have time to look into it.

They have changed indeed, see http://jdevelopment.nl/jsf-22/#220 and JAVASERVERFACES_SPEC_PUBLIC-220 for more details about this.


codylerum added a comment - 08/May/13 01:16 PM

This is one of the larger pain points for developers utilizing ajax so it is very unfortunate that we are likely looking at years for resolution. Can anything be done at this point to speed that up?

Currently this issue has almost 2x the votes of the next closest issue.


swathireddy12 added a comment - 22/Aug/13 11:51 AM

I am making use of JSF 1.2 version of jars (myfaces-api-1.2.9.jar ,myfaces-impl-1.2.9.jar,trinidad-api-1.2.13.jar,trinidad-impl-1.2.13.jar).
I am trying to retrieve the javax.faces.ViewState using the id attribute in a Javascript which works.

But i still don't see the id attribute in the loaded page
<input type="hidden" name="javax.faces.ViewState" value="!-14uywjgjai">

Could you please tell me if this is an issue with JSF 1.2 version as well?
Or once the page is rendered the id attribute associated with "javax.faces.ViewState" is not seen anymore.


werpu added a comment - 22/Aug/13 12:28 PM

No this is a JSF 2.x issue only. This has nothing to do with JSF 1.2.


balusc added a comment - 13/Jan/14 03:36 PM

This is one of the larger pain points for developers utilizing ajax so it is very unfortunate that we are likely looking at years for resolution. Can anything be done at this point to speed that up?

For exactly this reason, it has been added to OmniFaces 1.7: http://showcase.omnifaces.org/scripts/FixViewState