1 2 Previous Next 18 Replies Latest reply on Jan 31, 2011 9:29 AM by Stan Silvert

    RichFaces and ajaxQueues

    Eric Taieb Newbie

      Hi,

      After developping quite a few tests on many Richfaces ajax pages, it seems that whenever there is an ajaxQueue in the richfaces tag, the ajax event never gets fired by htmlunit.

      I have seen this behavior accross all the tests I wrote so far.

      As soon as I remove the ajaxQueue, everything works fine.

      Can anyone confirm this ?

      Thanks again for your help,


      Eric

        • 1. Re: RichFaces and ajaxQueues
          Eric Taieb Newbie

          More info.

          After exploring a bit further it seems the problem only occurs for checkBoxes with a4j:support and 'onchange' event.

          As far as I can tell, it doesn't seeme to be a problem with ListBoxes, inputBoxes etc.

          Thanks,

          --Eric

          • 2. Re: RichFaces and ajaxQueues
            Eric Taieb Newbie

            Well I need to correct my previous post.

            It does have problems with all controls.

            SelectOneMenu is giving the same behavoir. If I have an ajaxQueue then:

            [com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController] - Re-synchronized call to [URL]

            never gets called...

            • 3. Re: RichFaces and ajaxQueues
              Eric Taieb Newbie

              At the end seems that in my earlier post "RichFaces an onBlur" http://www.jboss.org/index.html?module=bb&op=viewtopic&t=153299

              the reason was ajaxQueues too.

              The sleep for X seconds between 'blurs' was not solving the issue, whereas removing the ajaxQueues was the solution...


              • 4. Re: RichFaces and ajaxQueues
                Stan Silvert Master

                Thanks for researching this. I've asked the HtmlUnit team about it.

                Stan

                • 5. Re: RichFaces and ajaxQueues
                  Eric Taieb Newbie

                  No problem.
                  Glad to help improve things.

                  • 6. Re: RichFaces and ajaxQueues
                    Andrei Markavtsov Apprentice

                    Hi,

                     

                    I've got the same problem.

                    You can try the following workaround:

                     


                    @Override
                    public void setUp() throws IOException
                    {
                    ........

                    this.webClient = jsfSession.getWebClient();

                    webClient.setScriptPreProcessor(new ScriptPreProcessor()
                    {
                    public String preProcess(HtmlPage page, String source, String name, HtmlElement context) 
                    {                    
                    // Fix problem: JSFUnit is failed if a4:queue used on the page  
                    if (source.contains("EventQueue.getQueue('org.richfaces.queue.global')")) {
                    return "";
                    }

                    }               
                    });

                    ......

                     

                    The sence is to avoid loading of script related to a4j:queue implementation.

                     

                    I'm not sure if this workaround works in all cases, because seems that script depends on tag's attributtes and a4j:queue position on the page.

                    But it works for global defined a4j:queue without any attributes specified.

                    • 7. Re: RichFaces and ajaxQueues
                      Bernard Labno Master

                      Any progress on this one? Seam base apps must use queues! Stan, save us, we beg You!

                      • 8. Re: RichFaces and ajaxQueues
                        Stan Silvert Master

                        Bernard Labno wrote:

                         

                        Any progress on this one? Seam base apps must use queues! Stan, save us, we beg You!

                        I don't know when I can get to it.  Can someone write up a full decription of the problem with lots of sample code?  If we can get it down to the HtmlUnit level that would be best.  Then we can turn it over to the HtmlUnit team and tell them exactly what doesn't work.  Those guys don't know much about RichFaces.

                         

                        Stan

                        • 9. Re: RichFaces and ajaxQueues
                          Stan Silvert Master

                          Update.  I'm working to get someone from the RichFaces team to help with this.  I think they will be able to work with the HtmlUnit team a lot more efficiently that I could.  It will be a couple of weeks before someone is freed up though.

                           

                          Stan

                          • 10. Re: RichFaces and ajaxQueues
                            Stan Silvert Master

                            Bernard,

                             

                            Can you try playing around with the waitForBackgroundJavaScript() and waitForBackgroundJavaScriptStartingBefore() methods?

                             

                            Nick from the RichFaces team says you can get around the problem that way.  See https://issues.jboss.org/browse/RFPL-953.  To get the WebClient object in JSFUnit, you just need to call JSFSession.getWebClient().

                             

                            Stan

                            • 11. Re: RichFaces and ajaxQueues
                              Bernard Labno Master

                              Stan, thank you for not forgetting this issue. I will investigate it today.

                              • 12. RichFaces and ajaxQueues
                                Stan Silvert Master

                                Bernard Labno wrote:

                                 

                                Stan, thank you for not forgetting this issue. I will investigate it today.

                                 

                                Hi Bernard,

                                 

                                Any feedback on this? 

                                 

                                Stan

                                • 13. Re: RichFaces and ajaxQueues
                                  Bernard Labno Master

                                  Ok, guys. Case is not trivial. There are a lot of caveats here to pay attention to.

                                  First let's talk about difference between waitForBackgroundJavaScript and waitForBackgroundJavaScriptStartingBefore. What I've experienced is that the first one waits fixed amount of time you tell it to (param). The latter will see if there is any javascript timeout, interval or ajax scheduled to be invoked within the time you pass in param. So if there is nothing to start between now and that period (cause sth has started already or will start but after that time from param) than that method will wait so it (interval or timeout) starts and then will wait for it to finish.

                                   

                                  Example #1: we call waitForBackgroundJavaScriptStartingBefore(3000) and there is javascript function which timeout will be triggered in 2 seconds and will take 5 seconds to execute. So  waitForBackgroundJavaScriptStartingBefore will block our thread for 2+5=7 seconds.

                                   

                                  Example #2: we call waitForBackgroundJavaScriptStartingBefore(3000)and there is javascript function which has already been invoked (i.e. 1 second before) and will take 4 seconds to execute. So waitForBackgroundJavaScriptStartingBefore will not block at all, cause that javascript function is not starting within next 3 seconds, but has already started.

                                   

                                  Both methods return number of javascript jobs still running at the moment of functions ending.

                                   

                                  So what we could do in our JSFUnit tests is to put waitForBackgroundJavaScript inside a loop.

                                   

                                  Here is an example #3:

                                   

                                   

                                  {code}

                                  public void testAjaxReRender() throws Exception {

                                          JSFSession jsfSession = new JSFSession("/");

                                          JSFClientSession client = jsfSession.getJSFClientSession();

                                          assertEquals(client.getElement("text").getAttribute("value"), client.getElement("watermarked").getAttribute("value"));

                                          final String text = "Johnny Bravo";

                                          logger.info("Setting text: " + text);

                                          client.setValue("text", text);

                                          int jobs;

                                          do {

                                              jobs = ((WebClient) jsfSession.getWebClient()).waitForBackgroundJavaScript(1000);

                                          } while (jobs > 0);

                                          assertEquals(text, jsfSession.getJSFServerSession().getManagedBeanValue("#{watermarkBean.text}"));

                                          assertEquals(null, jsfSession.getJSFServerSession().getManagedBeanValue("#{watermarkBean.watermarkedInput}"));

                                          assertEquals(text, client.getElement("text").getAttribute("value"));

                                          assertEquals(text, client.getElement("watermarked").getAttribute("value"));

                                      }{code}

                                   

                                  and facelet:

                                  {code:xml}<h:form id="theForm">

                                   

                                      <a4j:queue requestDelay="400" ignoreDupResponses="true"/>

                                   

                                      <h:outputLabel value="Watermark text" for="text"/>

                                      <h:inputText id="text" value="#{watermarkBean.text}" required="true">

                                          <a4j:support event="onchange" ajaxSingle="true" reRender="theForm"/>

                                      </h:inputText>

                                      <h:commandButton value="Submit"/>

                                      <br/><br/>

                                      <h:inputText id="watermarked" value="#{watermarkBean.watermarkedInput}">

                                          <watermark:watermark value="#{watermarkBean.text}"/>

                                      </h:inputText>

                                  </h:form>{code:xml}

                                   

                                  What happens in the test is we change value of #{watermarkBean.text} by submitting ajax request after changing value of component with id="text". This request should reRender component with id="watermarked" and due to nature of watermark, client side control representing component with id="watermarked" should have DOM attribute named "value" set to value of #{watermarkBean.text}.

                                  To make things harder we have default queue with requestDelay.

                                   

                                  This sample will work just great. As long as there is an ajax request running the waitForBackgroundJavaScript method will return 1 and will remain in loop. Notice that the thread will block for 1 second in each loop iteration.

                                   

                                  But! What if we have some a4j:poll running in the background! Then waitForBackgroundJavaScript will return 2 when ajax request submitting new value of "text" component is running and 1 when it's over, but there is still poll running.

                                  Ok. Here is a solution. Before changing value of "text" we will check how many jobs are running and will use that value in stead of 0 in loop condition. Here is the code:

                                  {code}

                                  public void testAjaxReRender() throws Exception {

                                          JSFSession jsfSession = new JSFSession("/");

                                          JSFClientSession client = jsfSession.getJSFClientSession();

                                          assertEquals(client.getElement("text").getAttribute("value"), client.getElement("watermarked").getAttribute("value"));

                                          final String text = "Johnny Bravo";

                                          int initialJobs = ((WebClient) jsfSession.getWebClient()).waitForBackgroundJavaScript(1000);

                                          logger.info("Initial jobs: " + initialJobs);

                                          client.setValue("text", text);

                                          int jobs;

                                          do {

                                              jobs = ((WebClient) jsfSession.getWebClient()).waitForBackgroundJavaScript(1000);

                                          } while (jobs > initialJobs);

                                          assertEquals(text, jsfSession.getJSFServerSession().getManagedBeanValue("#{watermarkBean.text}"));

                                          assertEquals(null, jsfSession.getJSFServerSession().getManagedBeanValue("#{watermarkBean.watermarkedInput}"));

                                          assertEquals(text, client.getElement("text").getAttribute("value"));

                                          assertEquals(text, client.getElement("watermarked").getAttribute("value"));

                                      }

                                  {code}

                                  and facelet:

                                  {code:xml}

                                  <h:form id="theForm">

                                   

                                      <a4j:poll ajaxSingle="true" interval="5000"/>

                                      <a4j:queue requestDelay="400" ignoreDupResponses="true"/>

                                   

                                      <h:outputLabel value="Watermark text" for="text"/>

                                      <h:inputText id="text" value="#{watermarkBean.text}" required="true">

                                          <a4j:support event="onchange" ajaxSingle="true" reRender="theForm"/>

                                      </h:inputText>

                                      <h:commandButton value="Submit"/>

                                      <br/><br/>

                                      <h:inputText id="watermarked" value="#{watermarkBean.watermarkedInput}">

                                          <watermark:watermark value="#{watermarkBean.text}"/>

                                      </h:inputText>

                                  </h:form>

                                  {code:xml}

                                  Ok. That should do. But...what if we have some custom javascript? Oh no! Now the real trouble comes. Check this out:

                                  {code:xml}

                                  <script type="text/javascript">

                                          function funky() {

                                              setTimeout(function(){alert(1);}, 10000);

                                          }

                                          funky();

                                  </script>

                                  <h:form id="theForm">

                                   

                                      <a4j:poll ajaxSingle="true" interval="5000"/>

                                      <a4j:queue requestDelay="400" ignoreDupResponses="true"/>

                                   

                                      <h:outputLabel value="Watermark text" for="text"/>

                                      <h:inputText id="text" value="#{watermarkBean.text}" required="true">

                                          <a4j:support event="onchange" ajaxSingle="true" reRender="theForm"/>

                                      </h:inputText>

                                      <h:commandButton value="Submit"/>

                                      <br/><br/>

                                      <h:inputText id="watermarked" value="#{watermarkBean.watermarkedInput}">

                                          <watermark:watermark value="#{watermarkBean.text}"/>

                                      </h:inputText>

                                  </h:form>

                                  {code:xml}

                                  Does it look scarry? Well this piece of code will not hurt us, cause it will increase both values of initialJobs and jobs variables. The problem doesn't even arise here (notice recurence):

                                  {code:xml}

                                  <script type="text/javascript">

                                          function funky() {

                                              setTimeout(funky, 10000);

                                          }

                                          funky();

                                      </script>

                                  {code:xml}

                                  Troubles start with setInterval inside recuring function:

                                  {code:xml}

                                  <script type="text/javascript">

                                          function funky() {

                                              setInterval(funky, 10000);

                                          }

                                          funky();

                                      </script>

                                  {code:xml}

                                  Now we will never get the number of jobs same as initialJobs value.

                                  Notice that simple setIntervale (not in recuring function) is OK. It will increase number of jobs by one but only once (it will be the same job):

                                  {code:xml}

                                  <script type="text/javascript">

                                          function funky() {

                                              document.getElementById("nothing");

                                          }

                                          setInterval(funky,1000);

                                      </script>

                                  {code:xml}

                                  So remember: no setInterval inside recuring methods!

                                  But..it's not that simple. Look at the picture and try to find the problem:

                                  {code:xml}

                                  <h:form id="theForm">

                                      <script type="text/javascript">

                                          function funky() {

                                              setTimeout(funky, 1000);

                                          }

                                          funky();

                                      </script>


                                      <a4j:poll ajaxSingle="true" interval="5000"/>

                                      <a4j:queue requestDelay="400" ignoreDupResponses="true"/>

                                   

                                      <h:outputLabel value="Watermark text" for="text"/>

                                      <h:inputText id="text" value="#{watermarkBean.text}" required="true">

                                          <a4j:support event="onchange" ajaxSingle="true" reRender="theForm"/>

                                      </h:inputText>

                                      <h:commandButton value="Submit"/>

                                      <br/><br/>

                                      <h:inputText id="watermarked" value="#{watermarkBean.watermarkedInput}">

                                          <watermark:watermark value="#{watermarkBean.text}"/>

                                      </h:inputText>

                                  </h:form>{code:xml}

                                  Have you found the bug? Take a look at reRender attribute of a4j:support...Found it now? Ah! Here it is! On initial page load we get 2 jobs. One is poll and the other one is funky timeout. Ok so we have 2 initial jobs. But when the ajax request finishes it re-renders entire form. This means a new funky is invoked and a new job is started. Now we have 3 jobs: 1 poll and 2 funkies. Now we will never be able to get back to number of 2 initial jobs and we're stucked inside our test's loop.

                                   

                                  Good solution could be adding totalTimeout condition to the loop.

                                          int jobs;

                                          long startTime = System.currentTimeMillis();

                                          do {

                                              jobs = ((WebClient) jsfSession.getWebClient()).waitForBackgroundJavaScript(1000);

                                          } while (jobs > initialJobs && startTime + 10*1000 > System.currentTimeMillis());

                                  This way we can even discover some JavaScript memory leaks!

                                   

                                  Ok. That's it. This is the end of my experiences with ajax queues and those funny htmlunit methods like waitForBackgroundJavaScript. What do you think about all this?

                                  • 14. RichFaces and ajaxQueues
                                    Stan Silvert Master

                                    I think Bernard is awesome.

                                     

                                    Do you think we can generalize this into some utility code that can be put into JSFUnit core?

                                     

                                    Stan

                                    1 2 Previous Next