2 Replies Latest reply on Jun 19, 2008 11:48 PM by freemarket

    Concurrent ajax-requests and synchronization of the view sta

    jan.ziegler

      I´m faced with a problem that concurrent ajax-request to the same view-context seems to lead to synchronization problems with the
      component tree of the corresponding UIViewRoot.

      My simplified scenario is like this:
      I have two components, both are containing a <a4j:jsfunction>. The JSFunction is responsible for loading my components content dynamically
      by using an action listener.

      The faclet-page might look like this:

      <ui:composition>
      <h:form>
      
       <h:panelGroup id="component1">
       <a4j:jsfunction name="getContent1ViaAjax()" actionListener="#{contentManager.getContentForComponent1}" reRender="component1" eventsQueue="queue1" ignoreDuplicateResponses="true" requestDelay="300" />
       </h:panelGroup>
      
       <h:panelGroup id="component2">
       <a4j:jsfunction name="getContent2ViaAjax()" actionListener="#{contentManager.getContentForComponent2}" reRender="component2" eventsQueue="queue2" ignoreDuplicateResponses="true" requestDelay="300"/>
       </panelGroup>
      
       <!-- start the ajax-requests during page load -->
       <script type="text/javascript">getContent1ViaAjax(); getContent2ViaAjax()</script>
      
      </h:form>
      <ui:composition>


      This is what is does:
      The page is geeting rendered and directly afterwards, there are two ajax-reuqests performed in parallel because of the java-script call to both functions. So far no problem. The action listeners (...getContentForComponent...) get called
      and the content of the components is added programmatically to the components in the INVOKE APPLICATION phase. By using reRender, the result is rendered correctly - both components are filled
      rendered filled up with their "dynamic" content.

      The problem starts when the dynamic added content is performing some further ajax action.
      As an example component1 might look like this after getting filled via the action listener:

      ...
      <h:panelGroup id="component1">
       <a4j:jsfunction name="getContent1ViaAjax()" actionListener="#{contentManager.getContentForComponent1}" reRender="component1" eventsQueue="queue1" ignoreDuplicateResponses="true" requestDelay="300" />
      
       <!-- programmatically added content (would be look like this in tag-style) -->
      
       <!-- input-component bound to "myBean" -->
       <h:inputText value="#{myBean.inputValue}" />
      
       <!-- save the input value and reload the content of the component by calling the jsfunction again via commandButton-click -->
       <!-- instead of using a a4j:commandButton I prefer to call my JSFunction to reload the content on the component (should be the same) -->
       <h:commandButton onClick="getContent1ViaAjax();return;" value="save value" />
      
      </h:panelGroup>
      ...
      


      After the initial parallel ajax load of the component´s content I´m sometimes (not always) faced to the problem that in the upper example the input-expression #{myBean.inputValue} isn´t bound to "myBean" (which is in session scope).
      Then, if the first request after this "initial load" (triggert via the commandButton) happens, an empty inputText is shown again even I entered something in the input-field.
      I did some debugging and realized that in this case the model ("myBean") isn´t updated in UPDATE MODEL VALUES phase. I did some further debugging on the view root and found the reason for this:
      There seem to be no <h:inputText>-Component in the ViewRoot at all but it was definitly added to component1 by the action listener.

      So I look at 3 request now: the 2 parallel requests (initial load of component content) and the third request (first reload of one component´s content):
      request1 = load content of compoment1 by calling getContent1ViaAjax()
      request2 = load content of compoment1 by calling getContent2ViaAjax()
      ...
      request3 = load content of compoment1 by calling getContent1ViaAjax() again for the first time after the "inital load"

      If I dump the view root (via a phase listener in the RENDER_RESPONSE phase) for each of the requests I can see that the view root in request1 contains the input-component.
      Request2 shows the view root without the input-component so modifications performed by request1 (if happend before in parallel) seem to be not visible here. This implicates that both requests use a copy of the (same) view root (anyone now how this is generally handled in JSF?).
      My conclusion is like this:
      Lets say the request2 needs a little bit longer than request1 than the "final" state for this view context seems to saved by request2 and therefor the changes made by request1
      seems to be "overwritten".
      This is why in request3 there is no input-component anymore after RESTORE VIEW based on the last saved state and therefor no value is updated in "myBean". Only now
      the input-compoment is added again through the action listener call. After this it seems to be available forever and everthing works.
      So the last running request seem to be responsible for saving the state of the view but if concurrent modification happenes by different request on the same view, they get lost.

      Now the bad thing is, that I almost evertime have to do an extra reload / request the use my dynamic (via ajax) constructed components in a correct way because only then, everything seems to be present in the view state.

      The following questions arises:
      1) (How) Is the view state of concurrent requests to the same view context (same viewId and performing a postback) synchronized?
      2) If it is not synchonized how can I synchronize it? I tried using the same eventQueue (+requestDelay) for all jsfunctions but it doesn´t seem to work with jsfunction well cause the second of the parallel requests is regared as "similar" and therefor skipped.
      Also I want the request to be performed in parallel and not in sequence as it would be then. I think this is one reasing for using ajax at all.
      3) Is it a JSF (I´m using MyFaces 1.2.1 / Facelets 1.1.14) problem or an Ajax4JSF-Problem?



      Hope someone can help me with this, I´m trying to solve this problem for several days now.
      Thanks!


        • 1. Re: Concurrent ajax-requests and synchronization of the view
          jan.ziegler

          Maybe the scenario above is a little bit to complex for explaining my problem ´cause nobody seems to have an answer so I try to clearify it a little bit:

          Think about a scenario where one performs 2 jsf requests to the same view (e.g. /index.xhtml) via ajax in parallel. This can easily be done by using a4j:function or a4j:poll.
          Let´s say both request arrive nearly at the same time on server side so they refer to the same view tree which is restored in RESTORE_VIEW phase at the beginning of the lifecyle.

          Now during lifecycle both request modify the view programatically in action- or action listener methods concurrently.

          At the end of each request, the response is rendered and after that (or between) the state of the view root is stored to the session (in STATE_SAVING_METHOD=server) by the AjaxStateManager in the form of viewID=state.

          When I look into the code this happens via the method saveStateInSession().

          But at this point their seems to be no "merging" between modifications made to the view tree in parallel. So the slower request of both is the last one saving the state of the view tree and therefor overwriting the modifications made by the faster request, which completed before.

          Should this be the normal behavior for parallel requests changing state on the same view tree?

          It´s hard to find anything in the web how this is handled in generall in JSF but I think it becomes really important when using ajax.

          So any ideas / comments on this?



          • 2. Re: Concurrent ajax-requests and synchronization of the view
            freemarket

            Jan,

            I experienced a similar situation today but from a different environment.
            I am using facelets. I have a screen that is rendering a
            tree on the left where the tree nodes paint the appropriate facelet
            on the right when the nodes are clicked using a a4j:include where the viewid is manipulated by the backing bean. The facelet on the right
            composites 2 facelets upper and lower using ui:include and the upper one composites another facelet via ui:include. This last one is rendered when a button is clicked on upper facelet. The outer most part of the right facelet has a bottom button that fires an action. If I make the popup facelet fire and invoke its workflow the bottom button no longer fires and tracing through the JBDS debugger, I see that the view root disappears below the facelet that is conditionally rendered in the top and appears in the view root when it is not conditionally rendered. So when the bottom button is clicked the eventQueue is traversed but no ActionEvent is fired. I suspect this has something to do with the viewstate but unclear what the underlying issue is as to why the viewroot is inconsistent with what is painted on the screen. I do have phasetracker output and this is
            RichFaces 3.1.6 GA with myfaces 1.1.5, facelets 1.1.3.

            Thanks,
            Henry