-
1. Re: RichFaces and ajaxQueues
teknologist Apr 7, 2009 3:32 AM (in response to teknologist)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
teknologist Apr 7, 2009 4:11 AM (in response to teknologist)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
teknologist Apr 7, 2009 4:32 AM (in response to teknologist)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
ssilvert Apr 7, 2009 7:56 AM (in response to teknologist)Thanks for researching this. I've asked the HtmlUnit team about it.
Stan -
5. Re: RichFaces and ajaxQueues
teknologist Apr 7, 2009 8:03 AM (in response to teknologist)No problem.
Glad to help improve things. -
6. Re: RichFaces and ajaxQueues
andrei_exadel Dec 14, 2009 5:04 AM (in response to teknologist)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
blabno Nov 10, 2010 10:10 AM (in response to andrei_exadel)Any progress on this one? Seam base apps must use queues! Stan, save us, we beg You!
-
8. Re: RichFaces and ajaxQueues
ssilvert Nov 10, 2010 2:59 PM (in response to blabno)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
ssilvert Nov 12, 2010 9:30 AM (in response to ssilvert)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
ssilvert Dec 28, 2010 3:57 PM (in response to ssilvert)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
blabno Jan 3, 2011 7:11 AM (in response to ssilvert)Stan, thank you for not forgetting this issue. I will investigate it today.
-
12. RichFaces and ajaxQueues
ssilvert Jan 10, 2011 8:14 AM (in response to blabno)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
blabno Jan 19, 2011 10:41 AM (in response to ssilvert)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
ssilvert Jan 19, 2011 4:48 PM (in response to blabno)I think Bernard is awesome.
Do you think we can generalize this into some utility code that can be put into JSFUnit core?
Stan