Is GWT truly AJAX: Loss of Focus in Firefox v1.0.8

83 views
Skip to first unread message

Rahul

unread,
Dec 14, 2006, 10:05:11 AM12/14/06
to Google Web Toolkit
Hi,

I have migrated an application from Struts to GWT because in Struts we
had a Loss of Focus everytime a page refreshes or rather reloads. My
application needs to update itself every 5 seconds.

In IE v6.0 GWT implementation has addressed the problem of losing focus
but in Firefox v1.0.8 it is flickering everytime when data refreshing
takes place.

Further rendering of Widgets in IE and firefox is not exactly same with
latter showing some unexpected behaviour. Though issues with few
Widgets have been addressed in latest release but still there are many
more to be addressed.

Also, few refresh calls in IE v6.0 are running afoul and we are getting
SocketTimeoutException on server side while the same code is running
without any such behaviour in Firefox v1.0.8.

Any comments/insight on this are highly appreciated.

Thanks,
Rahul

Dan Morrill

unread,
Dec 15, 2006, 1:49:00 PM12/15/06
to Google-We...@googlegroups.com

Hi, Rahul!

We can't really offer any insight into the problems you're having without the relevant information.

Can you provide more details as to what your application is doing, and how you are communicating with your server?  If you can, we might be able to offer some suggestions.

Thanks!

- Dan Morrill

Rahul

unread,
Dec 17, 2006, 2:40:23 PM12/17/06
to Google Web Toolkit
Hi Dan,

I am providing you the sample code that is I have coded for EntryPoint
class in GWT and which is having different behaviour for two browsers:

public class GetDataEntryPoint implements EntryPoint
{

private boolean myHasResponded = false;
private long myLastCallTime = 0;
private Timer myTimer = null;

private static ServiceAsync serviceAsync = (ServiceAsync)
GWT.create(Service.class);


public void onModuleLoad()
{

VerticalPanel inputPanel = new VerticalPanel();
// HERE GOES CODE TO CREATE WIDGETS FOR DISPLAY
// AND SET THEM IN inputPanel.
RootPanel.get().add(inputPanel);

// GET DATA FROM RPC CALL AND PUT IN WIDGETS THAT
// ARE SET IN inputPanel ABOVE.
fetchData();

// REFRESH DATA.
refreshData();
}

private void fetchData()
{
myLastCallTime = System.currentTimeMillis();

serviceAsync.getXYZ(param1, param2, new AsyncCallback() {
public void OnSuccess(Object result)
{
myHasResponded = true;

// CODE FOR PARSING RESULT, WHICH IS DATA
// FETCHED FROM SERVER IN THE FORM OF
// Collection. THIS Collection IS AN ArrayList AND
// CONTAINS OTHER ArrayList(s) WHICH
// FINALLY HAS String DATA AT LOWEST LEVEL.

// ITERATING OVER Collection OBTAINED TO FILL WIDGETS.

}
public void onFailure(Throwable caught)
{
myHasResponded = true;
}
});
}

private void refreshData()
{
myTimer = new Timer() {
public void run()
{
long currentTime = System.currentTimeMillis();

if (myHasResponded) {
myHasResponded = false;
fetchData();
}

// CALLING FALLBACK MECHANISM IF NO ACTIVITY IS
// REGISTERED FOR LAST 30 SECONDS.
else if ((currentTime - myLastCallTime) >30000) {
callFallBack();
}
}
};

// SCHEDULING THE TIMER TO RUN ONCE IN 5 SECONDS.
myTimer.scheduleRepeating(5000);
}

private void callFallBack()
{
myTimer.cancel();
myLastCallTime = System.currentTimeMillis();
refreshData();
fetchData();
}
}

I think this code is sufficient to understand the jist of what I am
doing to display data. This code does not have problem of focus loss in
IE v6.0 while in Firefox v1.0.8 this code is flickering every time it
refreshes data.

Also, the above code is deployed on jboss server. As far as server side
is concerned, in Firefox v1.0.8 it is running fine but when this page
is accessed on IE v6.0 one in hundred calls is running afoul and
throwing following exception at server side:

java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at
org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:747)

at
org.apache.coyote.http11.InternalInputBuffer$InputStreamInputBuffer.doRead(InternalInputBuffer.java:777)

at
org.apache.coyote.http11.filters.IdentityInputFilter.doRead(IdentityInputFilter.java:115)

at
org.apache.coyote.http11.InternalInputBuffer.doRead(InternalInputBuffer.java:712)

at org.apache.coyote.Request.doRead(Request.java:429)
at
org.apache.coyote.tomcat5.InputBuffer.realReadBytes(InputBuffer.java:290)

at
org.apache.tomcat.util.buf.ByteChunk.substract(ByteChunk.java:374)
at
org.apache.coyote.tomcat5.InputBuffer.read(InputBuffer.java:305)
at
org.apache.coyote.tomcat5.CoyoteInputStream.read(CoyoteInputStream.java:179)

at
com.google.gwt.user.server.rpc.RemoteServiceServlet.readPayloadAsUtf8(RemoteServiceServlet.java:530)

at
com.google.gwt.user.server.rpc.RemoteServiceServlet.doPost(RemoteServiceServlet.java:142)

at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

at javax.servlet.http.HttpServlet.service(HttpServlet.java:810)

at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:237).........


Further, there are few widgets which have different behaviour in two
browsers and one common problem is related to not proper working of
setWordWrap() function.

Thanks for your time.

Rahul.

Message has been deleted

mP

unread,
Dec 17, 2006, 4:04:32 PM12/17/06
to Google Web Toolkit
Your timer is not scheduled to happen once.. it will fire each and
every 5 seconds.

// SCHEDULING THE TIMER TO RUN ONCE IN 5 SECONDS.
myTimer.scheduleRepeating(5000);

it should be
myTimer.schedule(5000);

I would guess that the possibility occurs that the server is delayed
and still processing a request while a new request is fired. It would
be better to start a new timer when the respnse comes back ( in both
the success/failure methods). That way the client code would fire a new
requeset at most once every 5 seconds.

Rahul

unread,
Dec 18, 2006, 1:39:34 AM12/18/06
to Google Web Toolkit
Hi mP,

It is scheduled to fire each and every 5 seconds but it will not send a
new request until a response for previous request has come OR there is
no activity for last 30 seconds.
In latter case it will cancel the previous timer and fires a new timer
to repeat the same story.

Thanks,
Rahul

Dan Morrill

unread,
Dec 18, 2006, 11:34:08 AM12/18/06
to Google-We...@googlegroups.com
Hi, Rahul!

You should probably break your questions up into separate threads;  specifically, you might want to post about the setWordWrap() issues in a separate discussion thread.  Also, you should create a separate post describing the actual "flickering" you are seeing in Firefox.  I can't tell what you mean by that, but it looks unrelated to your RPC code.  My guess is that you are just seeing Firefox re-rendering widgets with new data, but it's impossible to be sure without a more accurate description.



Regarding your IE socket timeout issue, it looks like you are not resetting myHasResponded correctly.  Specifically, suppose you have a 30-second timeout occur.  This is probably because your server is taking a lot of time to return the data, or because the network was down, etc..  At that point, myHasResponded is false, so you go into callFallBack().  In there, you reset your timer and then call refreshData() and fetchData() immediately.  If the response came back (as either success or failure) in between, then you will get 2 calls to serviceAsync.getXYZ() in close succession, with a very good chance of one beginning before the previous one completes.  This could be the source of your Socket exceptions.  This is a rare condition which is why you only see it once every 100 or so calls, as you point out.  Firefox either has different timing so that it just never happens, or different behavior when restarting async requests.

Personally, I would refactor this design a little.  I would not cancel the timer, but instead just let it run.  There appears to be no real reason to have callFallBack() be a separate method, so I would remove that method and just have the body of the "else if" block in refreshData() be:
  myLastCallTime = System.currentTimeMillis();
  myHasResponded = false;
  fetchData();

Note that this is still starting a new serviceAsync.getXYZ() call, so you still might see socket errors on the server, but in general if it happens at all it is probably because the connection is broken already.

Note also that you don't really need to test for the restart anyway.  The onFailure() method will eventually be called when the request times out on its own.  If you choose to re-issue a request before an existing one doesn't complete, as you are here, then you will get weird results regardless.

For example, suppose you issue a request, and it takes 34 seconds to run.  In that case, you will issue a new request;  suppose that request completes immediately, so you process and display the data.  However, the first request will complete anyway 3 seconds later (it was just delayed) and then its onSuccess() handler will run, possibly with old data.

If you choose to re-issue requests like this, you should keep a handle to the old AsyncCallback instance so you can invalidate it somehow to prevent these situations from occurring.

Hope that helps!

- Dan

org.apache.coyote.http11.InternalInputBuffer.fill (InternalInputBuffer.java:747)

        at
org.apache.coyote.http11.InternalInputBuffer$InputStreamInputBuffer.doRead(InternalInputBuffer.java:777)

        at
org.apache.coyote.http11.filters.IdentityInputFilter.doRead (IdentityInputFilter.java:115)


        at
org.apache.coyote.http11.InternalInputBuffer.doRead(InternalInputBuffer.java:712)

        at org.apache.coyote.Request.doRead(Request.java:429)
        at
org.apache.coyote.tomcat5.InputBuffer.realReadBytes (InputBuffer.java:290)


        at
org.apache.tomcat.util.buf.ByteChunk.substract(ByteChunk.java:374)
        at
org.apache.coyote.tomcat5.InputBuffer.read(InputBuffer.java:305)
        at
org.apache.coyote.tomcat5.CoyoteInputStream.read (CoyoteInputStream.java:179)

        at
com.google.gwt.user.server.rpc.RemoteServiceServlet.readPayloadAsUtf8(RemoteServiceServlet.java:530)

        at
com.google.gwt.user.server.rpc.RemoteServiceServlet.doPost (RemoteServiceServlet.java:142)


        at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

        at javax.servlet.http.HttpServlet.service(HttpServlet.java:810)

        at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter (ApplicationFilterChain.java:237).........

Rahul Singhal

unread,
Dec 19, 2006, 4:21:46 AM12/19/06
to Google-We...@googlegroups.com, Dan Morrill

Hi Dan,

Thanks for reply.
I thought of mentioning all the issues related to browser incompatibilities in same thread. Going forward I will put them under separate threads.
 
  >>  My guess is that you are just seeing Firefox re-rendering widgets with new data, but  it's impossible to be sure without a more accurate description.
 
By flickering, I mean to say that every 5 second when there is a code refresh Firefox repaints the whole page which appears as if it is flickering, even if there is no change in display data. While in IE refreshing does not seems like it is repainting the page. Further, until and unless there is a change in content of display data IE does not seem to refresh it every 5 second (the refresh time set for page) while Firefox does. Let me know if this description is sufficient.
 
  >> Regarding your IE socket timeout issue, it looks like you are not resetting myHasResponded correctly.
Is there a better way to handle the scenario.
 
  >>If you choose to re-issue requests like this, you should keep a handle to the old AsyncCallback instance so you can invalidate it somehow to prevent these situations from occurring.
I think GWT should provide us with a handle for the request sent, so that we can invalidate the previous request in 30 seconds (as a part of failsafe strategy) before sending a new request. It will cover all the possible scenario of getting weird results. OR we should have a mechanism to set time-out for a RPC call - Something similar to what latest version of RequestHandler class have (setTimeoutMillis).
 
I was cancelling the timer with the assumption that it might invalidate the thread and thus invalidate the request attached to it. For this reason only, I have a separate callFallBack() method.
 
So the best way to handle such situations effectively would be to have a mechanism in place where we can decide the fate of request before sending it, like by setting a time-out time, OR we should have a way to invalidate a request at later stage (, i.e, before firing a new request in current case).
 
Currently, I cannot think of any other proper way of avoiding this socketTimeoutException.
 
Thanks,
Rahul
 
org.apache.coyote.tomcat5.InputBuffer.read (InputBuffer.java:305)

        at
org.apache.coyote.tomcat5.CoyoteInputStream.read (CoyoteInputStream.java:179)

        at
com.google.gwt.user.server.rpc.RemoteServiceServlet.readPayloadAsUtf8(RemoteServiceServlet.java :530)

        at
com.google.gwt.user.server.rpc.RemoteServiceServlet.doPost (RemoteServiceServlet.java:142)

        at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

        at javax.servlet.http.HttpServlet.service (HttpServlet.java:810)

Dan Morrill

unread,
Dec 19, 2006, 9:51:33 AM12/19/06
to Rahul Singhal, Google-We...@googlegroups.com
On 12/19/06, Rahul Singhal <rahuls...@gmail.com> wrote:

I thought of mentioning all the issues related to browser incompatibilities in same thread. Going forward I will put them under separate threads.

Thanks, Rahul!  That will help us keep all the messages straight.
 

By flickering, I mean to say that every 5 second when there is a code refresh Firefox repaints the whole page which appears as if it is flickering, even if there is no change in display data. While in IE refreshing does not seems like it is repainting the page. Further, until and unless there is a change in content of display data IE does not seem to refresh it every 5 second (the refresh time set for page) while Firefox does. Let me know if this description is sufficient.

Ah, I see.  Generally speaking, this behavior is browser dependent.  Some browsers will repaint form elements every time you assign a value, whether the new value is the same as the old, or not.  That Firefox version ( 1.0.8) you are testing with is rather old, so it may simply be that Firefox is repainting every time, where IE only repaints where necessary.

Have you tried modifying your code that processes your response data and assigns the data to the widgets, to do a comparison first before doing the actual assignment?  I am thinking of something like:

if (widget.value != newValue) {
    widget.value = newValue;
}
 
If my hypothesis is correct, that should eliminate the flickering/repainting problem you are seeing.


  >> Regarding your IE socket timeout issue, it looks like you are not resetting myHasResponded correctly.
Is there a better way to handle the scenario.

Well, if you set myHasResponded to false in your current callFallBack() then I think you might at least clear up the one IE-related Socket exception you are seeing.  There might or might not be other exceptions.

I will think about your situation some more and see if I can come up with any other suggestions.
 

I think GWT should provide us with a handle for the request sent, so that we can invalidate the previous request in 30 seconds (as a part of failsafe strategy) before sending a new request. It will cover all the possible scenario of getting weird results. OR we should have a mechanism to set time-out for a RPC call - Something similar to what latest version of RequestHandler class have (setTimeoutMillis).

This is not a bad idea.  Can you go enter this as an enhancement request in the Issue Tracker?  The URL is: http://code.google.com/p/google-web-toolkit/issues/list

Thanks!

- Dan Morrill

Rahul Singhal

unread,
Dec 20, 2006, 2:54:47 AM12/20/06
to Dan Morrill, Google-We...@googlegroups.com
Hi Dan,
 
Thanks once again for your quick response.
 
From our discussion, I think there are two ways of dealing with this situation of flickering,
 
1) check if response data has changed and do the repainting of widget on that basis.
2) check if the value of particular widget has changed or not since last painting. (The way suggested by you)
 
Both of these have their own utility. If suppose response data size is static (means, the table size in which you are displaying data is static) then it makes more sense to go by 2 option. But if your datasize is changing then going with first option would be better as second would be an overkill. But it would be successful nonetheless.
 
>>Well, if you set myHasResponded to false in your current callFallBack() then I think you might at least clear up the one IE-related Socket exception you are seeing.  There might or might not be other exceptions.
Could you make it more clear that why you want to set myHasResponded to false in callFallBack(), as anyway it is false then only it is coming inside callFallBack().
 
Thanks,
Rahul
 

Dan Morrill

unread,
Dec 20, 2006, 9:22:51 AM12/20/06
to Google-We...@googlegroups.com
On 12/20/06, Rahul Singhal <rahuls...@gmail.com> wrote:
Thanks once again for your quick response.

No problem, Rahul!
 

From our discussion, I think there are two ways of dealing with this situation of flickering,
 
1) check if response data has changed and do the repainting of widget on that basis.

This is actually not something GWT can do.  If you assign a value to a GWT widget, that assignment is pushed through and done in JavaScript.  GWT does not track the states of the widgets in "Java space", but rather immediately pushes state change events through to "JavaScript space".

It is the browser that is repainting the widgets, and the browser makes its own decision as to when that is necessary.  It appears that Firefox 1.0.8 always repaints any time an assignment is done, whereas later versions of Firefox do not.  (If I recall correctly.)

So, option 1) is not really an option at all;  if you want to change the repaint behavior you will have to upgrade Firefox.  If you can't do that, then you should try 2).
 

Could you make it more clear that why you want to set myHasResponded to false in callFallBack(), as anyway it is false then only it is coming inside callFallBack() .

In the code you posted, refreshData() and fetchData() both test the value of myHasResponded.  refreshData() meanwhile also calls fetchData().  But, refreshData() also calls fetchData() after testing myHasResponded, meaning that in some situations you can end up with having two calls to fetchData() executing immediately back to back, with the second call possibly causing the first to fail and generating the exception you are seeing on the server. (I don't know for sure that this is happening, but it is my hypothesis.)

If you set myHasResponded to false in callFallBack() before you call the other methods, then you will prevent this back-to-back execution.

I hope that's more clear!

- Dan Morrill

Rahul Singhal

unread,
Dec 20, 2006, 10:31:07 AM12/20/06
to Google-We...@googlegroups.com
Hi Dan,
 
I think there is some confusion. Kindly permit to elaborate it. When you say:
 
>>In the code you posted, refreshData() and fetchData() both test the value of myHasResponded. 
 
No, only refreshData() checks for value of myHasResponded to decide whether to call fetchData() or not, depending upon whether previous call has returned or not.
 
>>refreshData() meanwhile also calls fetchData().  But, refreshData() also calls fetchData() after testing myHasResponded, meaning that in some situations you can end up with having two calls to fetchData() executing immediately back to back
No, both refreshData() and callFallBack() will not make back to back call. Let me execute it step by step:
 
1) Initially, myHasResponded = false and I make a call to fetchData() and refreshData() back to back from onModuleLoad().
 
2) Since myHasResponded is false, refreshData() will not make a call to fetchData(). It will only make call to fetchData() when previous call returns, i.e., when previous call will return either inside  onSuccess() or onFailure(), where I will set myHasResponded=true;.
 
3) Now, in next cycle of 5 seconds when Timer is scheduled to run it will find myHasResponded as true and enter the if block in run() method of Timer. Here I am first setting myHasResponded = false and then calling fetchData().
 
4) So far it is fine and I think we both are on same page. Now we can discuss the only possible scenario when callFallBack() will be called from refreshData().
 
5) Say things are fine till 5th call and it returned well within time and Timer is ready to fire the 6th call to fetchData(). Now, as always, before calling fetchData() I am setting myHasResponded = false . Here, I have fired the 6th call after setting myHasResponded=false.
 
6) 5 seconds have passed and Timer is ready to fire the next call, i.e., 7th call but previous one has not returned back. Anyway, its time for Timer to execute run() method so it enters run() method. Here since 6th call has not returned back, therefore myHasResponded is still false(because the only place where we set myHasResponded as true is inside onSuccess() and onFailure() methods).. Thus, it will not enter if block ; rather it will enter in else-if block where it will invoke callFallBack(). Remember myHasResponded is still false.
 
7) Enter callFallBack() it will first cancel Timer in the hope that associated request will also die down. Now it will invoke refreshData() but since myHasResponded is still false its if block will not be executed and also it doesn't satisfy the condition of else-if block so even that will not be executed.
 
8) It will make a fresh call to fetchData() which will hopefully return successfully either in onSuccess() or onFailure() setting myHasResponded = true (the only places where we set myHasResponded as true).
 
Hope this will make things more clear.
 
>>>>1) check if response data has changed and do the repainting of widget on that basis.
 
By repainting of widget I mean to say that if your response data has not changed you can skip the assignment step. If there is no new assignment to widget then it will not register any change in corresponding JavaScript. And if there is no any change in JavaScript then browser will certainly not paint the page.
 
 
Thanks,
Rahul

Dan Morrill

unread,
Dec 20, 2006, 1:51:00 PM12/20/06
to Google-We...@googlegroups.com

Thanks for entering that bug, Rahul!  Also, thanks for elaborating on the call sequence of your code design.  I do have one more comment for you down below, though.

On 12/20/06, Rahul Singhal <rahuls...@gmail.com> wrote:
7) Enter callFallBack() it will first cancel Timer in the hope that associated request will also die down. Now it will invoke refreshData() but since myHasResponded is still false its if block will not be executed and also it doesn't satisfy the condition of else-if block so even that will not be executed.

This is really at the heart of the phenomenon I was pointing out.  Timers in JavaScript are just events, meaning that when you use a timer, the browser just fires an event and drops it into the JavaScript event queue.  Canceling a Timer un-registers that event from the JavaScript event system, meaning that the Timer.run() will never be fired again; but it has no effect on any other events (such as an RPC call completing) that are already enqueued, or that may fire soon.

That means you can have a sequence of events that looks like this:

1. Timer event fires with a 30-second timeout, so run() is executed and callFallBack() will be run
2. While run() is executing, previous RPC call finally completes, so onSuccess() is enqueued and will run next
3. run() calls callFallBack()/fetchData()/refreshData() exactly as you describe, which starts another RPC call
4. onSuccess() runs from #2, sets myHasResponded to true
5. RPC call from #3 completes quickly, onSuccess() runs again (say, 3 seconds later)
6. Timer run() runs again

In other words:  you can have a case where onSuccess() will run twice, in between calls to the Timer's run() method.  If you change the sequence ever so slightly, so that the onSuccess from #2 completes just a little later, you can have a situation where you start a second RPC call before the first has completed.  Depending on the browser, this might cause the first to fail, be shut down early, etc.

So, that's what I think may be happening with Internet Explorer.  I also agree with you that the right way to solve this is to add an abort() method to RPC calls, which calls the underlying XMLHTTPRequest.abort () method in JavaScript.  Ultimately what you have to do is prevent the onSuccess() event in #4 from ever firing, and it does indeed look like an abort() or at least ignore() method is the only reliable way to accomplish this.

I think it might be possible to minimize or reduce this effect by refactoring your design a little, which is why I suggested it earlier. However, I'm not 100% sure you can completely eliminate the exceptions in your server logs without some help from GWT.

So -- thanks again for entering that bug!  I hope this conversation has been useful to you...

- Dan Morrill

Rahul Singhal

unread,
Dec 21, 2006, 10:35:12 AM12/21/06
to Google-We...@googlegroups.com, Dan Morrill

Hi Dan,

It was quite an insightful discussion, but we can only conclude that problem can be solved only when we have a mechanism to get decide fate of request sent.

I got your point of setting myHasResponded=false in callFallBack(), but then it is just our assumption that previous call will not return between setting myHasResponded=false in callFallBack() and the execution of subsequent statement. Practically, it can come up anytime and things may run afoul. But yeah setting myHasResponded=false at some important points will lower the probability of weird results.

Thanks for your time.

Rahul

Reply all
Reply to author
Forward
0 new messages