ICEfaces
  1. ICEfaces
  2. ICE-8510

ViewState element is not added to updated form

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: 3.0, EE-3.0.0.GA, 3.1
    • Fix Version/s: EE-3.0.0.GA_P01, 3.2
    • Component/s: Bridge, Framework
    • Labels:
      None
    • Environment:
      ICEfaces

      Description

      While testing the chat-portlet example, I noticed that chat would work only intermittently. The problem looks to be related to how the ViewState element of certain forms is applied/updated/removed. Further testing reveals that it's not specific to portlets. The chat sample that the chat-portlet is based on shows the same issue.

      To replicate:

      1) Build and deploy the core/chat app to Tomcat
      2) Open 1 browser instance (e.g. Firefox) and navigate to the chat application (e.g. http://localhost:8080/chat/ )
      3) Enter a handle and login to the chat
      4) Open a second browser instance (e.g. Chrome) and navigate to the chat application
      5) Enter a handle and login to the chat. At this point, the chat log should show in both browsers that the 2nd user has joined the chat.
      6) Return to the first browser, enter a chat message, and click send. The message never gets sent and the chat log does not show any updates.

        Activity

        Hide
        Deryk Sinotte added a comment -

        I tested back a couple of versions and the behaviour seems to be there in some form since at least the 3.0.0 release. It's manifests slightly differently in that push may stop working and not recover after a page reload. The basics of the issue on the current trunk are:

        When the page first loads, we check the ViewStates on the page using the browser's JavaScript console. It correctly reports the proper ViewState element with the right value for each of the 3 forms on the page:

        document.getElementsByName('javax.faces.ViewState');
        [
        <input type=​"hidden" name=​"javax.faces.ViewState" id=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" autocomplete=​"off">​
        ,
        <input type=​"hidden" name=​"javax.faces.ViewState" id=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" autocomplete=​"off">​
        ,
        <input type=​"hidden" name=​"javax.faces.ViewState" id=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" autocomplete=​"off">​
        ]

        After logging in, we check again. The ViewStates are there with the correct values. However, they have been "fixed" by our code and are now missing the id and autocomplete attributes:

        document.getElementsByName('javax.faces.ViewState');
        [
        <input name=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" type=​"hidden">​
        ,
        <input name=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" type=​"hidden">​
        ,
        <input name=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" type=​"hidden">​
        ]

        After logging in from a second browser, the first push is triggered which results in an update with a message saying that a new user has joined. However, as part of this push related update, the ViewState for the client form (not our own hidden forms) is gone. Either removed or not replaced:

        document.getElementsByName('javax.faces.ViewState');
        [
        <input name=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" type=​"hidden">​
        ,
        <input name=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" type=​"hidden">​
        ]

        Any subsequent request from that form now result in a full ViewRoot or ViewBody update since no ViewState was sent with the request:

        <?xml version='1.0' encoding='UTF-8'?>
        <partial-response><changes><update id="javax.faces.ViewBody"><![CDATA[<body><span id="_t9">.....

        Show
        Deryk Sinotte added a comment - I tested back a couple of versions and the behaviour seems to be there in some form since at least the 3.0.0 release. It's manifests slightly differently in that push may stop working and not recover after a page reload. The basics of the issue on the current trunk are: When the page first loads, we check the ViewStates on the page using the browser's JavaScript console. It correctly reports the proper ViewState element with the right value for each of the 3 forms on the page: document.getElementsByName('javax.faces.ViewState'); [ <input type=​"hidden" name=​"javax.faces.ViewState" id=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" autocomplete=​"off">​ , <input type=​"hidden" name=​"javax.faces.ViewState" id=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" autocomplete=​"off">​ , <input type=​"hidden" name=​"javax.faces.ViewState" id=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" autocomplete=​"off">​ ] After logging in, we check again. The ViewStates are there with the correct values. However, they have been "fixed" by our code and are now missing the id and autocomplete attributes: document.getElementsByName('javax.faces.ViewState'); [ <input name=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" type=​"hidden">​ , <input name=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" type=​"hidden">​ , <input name=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" type=​"hidden">​ ] After logging in from a second browser, the first push is triggered which results in an update with a message saying that a new user has joined. However, as part of this push related update, the ViewState for the client form (not our own hidden forms) is gone. Either removed or not replaced: document.getElementsByName('javax.faces.ViewState'); [ <input name=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" type=​"hidden">​ , <input name=​"javax.faces.ViewState" value=​"8786015529719486675:​6375718628450418134" type=​"hidden">​ ] Any subsequent request from that form now result in a full ViewRoot or ViewBody update since no ViewState was sent with the request: <?xml version='1.0' encoding='UTF-8'?> <partial-response><changes><update id="javax.faces.ViewBody"><![CDATA[<body><span id="_t9">.....
        Hide
        Deryk Sinotte added a comment -

        So my current theory is that, when the push update is received, the update payload an update for the whole form and and the ViewState value but does not include the ice.fixViewState script. Without this script, the ViewState does not get added back as an input element to the newly updated form. The next client interaction with the form then does not include a ViewState and JSF sees this as a "new" view and proceeds to provide a ViewBody or ViewRoot style update to replace the contents of the entire view.

        Show
        Deryk Sinotte added a comment - So my current theory is that, when the push update is received, the update payload an update for the whole form and and the ViewState value but does not include the ice.fixViewState script. Without this script, the ViewState does not get added back as an input element to the newly updated form. The next client interaction with the form then does not include a ViewState and JSF sees this as a "new" view and proceeds to provide a ViewBody or ViewRoot style update to replace the contents of the entire view.
        Hide
        Deryk Sinotte added a comment -

        So from what I can tell, when a request is initiated due to a push request, the resulting request doesn't fire any (or at best a small subset) of the listeners e.g. onAfterUpdate. Depending on the scope of the updates, it might work out. But if an update is scoped to a form, or is a larger update that encapsulates one or more forms, then not calling the listeners causes the problem described above.

        The reason is that, when a form is replaced, it doesn't yet include the hidden input ViewState element. That's typically added after the fact during a function registered to listen for onAfterUpdate events. Since the push request/response doesn't trigger these for some reason, the ViewState doesn't get re-applied and the next interaction a form that has a missing ViewState will lead to a full ViewState or ViewBody style update.

        There may be other side effects from not calling our various listeners, but this is the one that is causing this problem. It also explain why it seems intermittent since it depends on what gets updated (the form or just the internal contents of the form), and the type of request that is triggering the update (user vs push). This leads to behaviours which may seem odd - for example push updates continue to work because our special "push" form still has a valid ViewState.

        Show
        Deryk Sinotte added a comment - So from what I can tell, when a request is initiated due to a push request, the resulting request doesn't fire any (or at best a small subset) of the listeners e.g. onAfterUpdate. Depending on the scope of the updates, it might work out. But if an update is scoped to a form, or is a larger update that encapsulates one or more forms, then not calling the listeners causes the problem described above. The reason is that, when a form is replaced, it doesn't yet include the hidden input ViewState element. That's typically added after the fact during a function registered to listen for onAfterUpdate events. Since the push request/response doesn't trigger these for some reason, the ViewState doesn't get re-applied and the next interaction a form that has a missing ViewState will lead to a full ViewState or ViewBody style update. There may be other side effects from not calling our various listeners, but this is the one that is causing this problem. It also explain why it seems intermittent since it depends on what gets updated (the form or just the internal contents of the form), and the type of request that is triggering the update (user vs push). This leads to behaviours which may seem odd - for example push updates continue to work because our special "push" form still has a valid ViewState.
        Hide
        Deryk Sinotte added a comment -

        Push initiated requests do not trigger the various listeners in the bridge (probably on purpose as icepush shouldn't know about ice faces). To check I simply added the following script to the bottom of a page. The console logs all these when a client-initiated Ajax request is fired, but nothing when it's a push initiated request:

        <script>
        var beforeSubmitCallback = function ()

        { console.warn('*** before submit called'); }

        ice.onBeforeSubmit(beforeSubmitCallback);

        var beforeUpdateCallback = function ()

        { console.warn('*** before update called'); }

        ice.onBeforeUpdate(beforeUpdateCallback);

        var afterUpdateCallback = function ()

        { console.warn('*** after update called'); }

        ice.onAfterUpdate(afterUpdateCallback);
        </script>

        We have a number of things registered with these types of listeners to fix things up in the page. One of these things is to update or append a ViewState element if it's changed or went missing for some reason.

        Because of #1 and #2, if a push update happens to include a form as all or part of the update, then the ViewState doesn't get appended back in. Other tasks that rely on these listeners (like removing listeners?) will also not get triggered.

        So the problem we are having in the chat is that the push update just happened to replace the form and then, because the listeners don't get fired, the ViewState doesn't get re-applied. I assume that push is purposefully designed not to call these listeners but there is at least this one thing we rely on that causes a problem. Not sure if there are cases where the other listeners not running might also cause a problem.

        The workaround is to design the page in such a way as to try and avoid having a form updated at all but I don't think we can count on that being done all the time.

        Show
        Deryk Sinotte added a comment - Push initiated requests do not trigger the various listeners in the bridge (probably on purpose as icepush shouldn't know about ice faces). To check I simply added the following script to the bottom of a page. The console logs all these when a client-initiated Ajax request is fired, but nothing when it's a push initiated request: <script> var beforeSubmitCallback = function () { console.warn('*** before submit called'); } ice.onBeforeSubmit(beforeSubmitCallback); var beforeUpdateCallback = function () { console.warn('*** before update called'); } ice.onBeforeUpdate(beforeUpdateCallback); var afterUpdateCallback = function () { console.warn('*** after update called'); } ice.onAfterUpdate(afterUpdateCallback); </script> We have a number of things registered with these types of listeners to fix things up in the page. One of these things is to update or append a ViewState element if it's changed or went missing for some reason. Because of #1 and #2, if a push update happens to include a form as all or part of the update, then the ViewState doesn't get appended back in. Other tasks that rely on these listeners (like removing listeners?) will also not get triggered. So the problem we are having in the chat is that the push update just happened to replace the form and then, because the listeners don't get fired, the ViewState doesn't get re-applied. I assume that push is purposefully designed not to call these listeners but there is at least this one thing we rely on that causes a problem. Not sure if there are cases where the other listeners not running might also cause a problem. The workaround is to design the page in such a way as to try and avoid having a form updated at all but I don't think we can count on that being done all the time.
        Hide
        Ken Fyten added a comment -

        Since 3rd party application use these JS callbacks to trigger their own activity indicators, and they do not want these firing for Push updates, we cannot have some of these firing for push updates. I would recommend that we profile the behavior of ICEfaces 3.0 to see which JS callbacks are called for push updates and align with those.

        Show
        Ken Fyten added a comment - Since 3rd party application use these JS callbacks to trigger their own activity indicators, and they do not want these firing for Push updates, we cannot have some of these firing for push updates. I would recommend that we profile the behavior of ICEfaces 3.0 to see which JS callbacks are called for push updates and align with those.
        Hide
        Mircea Toma added a comment -

        Modified bridge to invoke onBeforeSubmit, onBeforeUpdate and onAfterUpdate callbacks even when updates are retrieved due to push notifications or polling.

        Show
        Mircea Toma added a comment - Modified bridge to invoke onBeforeSubmit, onBeforeUpdate and onAfterUpdate callbacks even when updates are retrieved due to push notifications or polling.
        Hide
        Deryk Sinotte added a comment -

        As per Ken's comment above though, is there a way to ensure that callbacks done for UI blocking or notification purposes are not called? I thought we had added a parameter at one point so that the callbacks could determine whether it was push or not and then execute based on that.

        Show
        Deryk Sinotte added a comment - As per Ken's comment above though, is there a way to ensure that callbacks done for UI blocking or notification purposes are not called? I thought we had added a parameter at one point so that the callbacks could determine whether it was push or not and then execute based on that.
        Hide
        Deryk Sinotte added a comment -

        I'm going to close this and chalk it up to some caching issues. I added some logging to the client scripts to ensure that the fix was in and the listeners were indeed being called after a push. Once I had done this, I could see my logging in the client consoles and the problem went away. I can no longer reproduce.

        Show
        Deryk Sinotte added a comment - I'm going to close this and chalk it up to some caching issues. I added some logging to the client scripts to ensure that the fix was in and the listeners were indeed being called after a push. Once I had done this, I could see my logging in the client consoles and the problem went away. I can no longer reproduce.
        Hide
        Mircea Toma added a comment -

        The onBeforesubmit callbacks can define a second parameter that has its value set to true when the submit was initiated by the user. The parameter is set to false when the submit was triggered by a push notification or polling.
        This way the blockUI feature or the busy indicator know when to enable their functionality.

        Show
        Mircea Toma added a comment - The onBeforesubmit callbacks can define a second parameter that has its value set to true when the submit was initiated by the user. The parameter is set to false when the submit was triggered by a push notification or polling. This way the blockUI feature or the busy indicator know when to enable their functionality.

          People

          • Assignee:
            Mircea Toma
            Reporter:
            Deryk Sinotte
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: