JDK-4138263 : Regression in lightweight dispatcher: no longer delivers events synchronously
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.1.6
  • Priority: P2
  • Status: Closed
  • Resolution: Not an Issue
  • OS: solaris_2.5.1,windows_nt
  • CPU: x86,sparc
  • Submitted: 1998-05-14
  • Updated: 1998-08-13
  • Resolved: 1998-08-13
Related Reports
Relates :  
Description
It is not possible to call
requestFocus()
on two different components in response to the same event without completely
screwing up AWT's notion of the focused component.  Unfortunately, EWT needs
to be able to do this in a few different places (eg. in a editable Grid,
clicking on a cell first calls requestFocus() on the Grid, then on the
cell editor).  This also currently effects lightweight windows due to some 
focus management code which gets called when activating a window. 

	(EWT is a lightweight components graphics library)

More details:

Here's our understanding of the JDK 1.1.6 focus problems: 

Apparently JavaSoft has decided to change focus event delivery for
lightweight components to be asynchronous (focus events are now queued
instead of dispatched synchronously).  

 - The focused component is redundantly stored in both the Window and the
   LightweightDispatcher (one of these per heavy weight component).

 - When a lightweight component requests the focus, this request is 
   propagated to the nearest LightweightDispatcher above the lightweight
   component via Container.proxyRequestFocus().

 - This results in a call to LightweightDispatcher.setFocusRequest() for
   the requesting lightweight component.

 - In setFocusRequest(),  the Window's focus owner is retrieved via 
   Window.getFocusOwner(). (This just returns the Window's stored focus
   owner.)

 - If the window's focus owner matches the lightweight dispatcher's
   focus owner, the lightweight dispatcher queues focus lost/gained events 
   for the old/new focus components and sets its focus owner to the new
   focus component.  The Window is not updated immediately - it will update
   its focus owner when it receives the focus gained event for the new
   focus component (this now happens asynchronously).

In most cases, this works out ok.  However, since the lightweight
dispatcher's focus component is updated synchronously and the Window's
focus component is updated asynchronously, it is easy for these two
values to become out of sync.  In this case, the Lightweight
dispatcher refuses to change the focus, as it seems that a
prerequestite for a focus change to take place is that the
LightweightDispatcher's focus component must be equal to the Window's
focus component.  We get into this state when the focus is requested
on two different components while processing a single event.

This problem is easily reproduced with an editable Grid.  On a mouse
press, two things happen:

  - The Grid requests the focus to itself,

and then, when startCellEdit() is called

  - The Grid requests the focus to move the the cell editor

Both of these focus changes occur in response to the same mouse
pressed event.

After the first call to request focus (on the Grid), AWT is in the
following state:

 - LightweightDispatcher's focus == Grid
 - Window's focus == the old focus owner - whatever used to have the focus
 - FOCUS_LOST/FOCUS_GAINED events are posted at the front of the EventQueue

Now, when we call requestFocus() on the editor component, the
LightweightDispatcher checks to see whether its focus component
matches the Window's focus component.  Since the event dispatcher has
not had a chance to deliver the focus change events to the Window (how
could it?  we are doing all of this processing in the event dispatcher
thread in response to a single mouse event.), this test fails and the
focus is not updated.  (Actually, in this case the
LightweightDispatcher quietly updates its own focus owner to the
requesting component, but no focus events are generated and the Window
never finds out about the change.)

I haven't been able to come up with any generic work around for this
problem.  We may be able to work around the problem on a case by case
basis by eliminating cases where requestFocus() is called on two
different components in response to a single event.  For example, we
can update the Grid such that it does not immediately call
requestFocus on its editor component, but instead defers this until it
receives a FOCUS_GAINED event.  This approach only works if we know
which component is supposed to have the focus and we are sure that a
focus gained event is pending.  We can probably apply the same work
around to other cases, but I think it is going to be very hard to get
everything right.  Basically, this means looking at *every* call to
requestFocus() in both EWT code and client code and deciding whether
it may be called in a thread for which requestFocus() has already been
called on another component.  

As far as a fix for JavaSoft... I don't think there is a trivial
solution.  Basically they need to make sure that their
LightweightDispatcher and Window stay in sync, which means changing
the way their focus events are dispatched.  See also 4131761.

---------------------------------------------------------------

Test case is attached.    It can also be found in 
~mrm/Oracle/bugs/jdk/4138263

marianne.mueller@Eng 1998-06-09

------------------------------------------------------------------------------

Comments
PUBLIC COMMENTS Focus events are queued (as of 1.1.6) instead of delivered synchronously.
10-06-2004

EVALUATION Customer's description is mostly right. The problem is that focusOwner is updated when Component receives a FOCUS_GAINED event, but this occurs asynchronously and so will not have happened before the second requestFocus. One possible solution may be to defer the entire setFocusRequest processing to be an ActiveEvent on the event queue. Alternatively, focusOwner could be updated synchronously in LightweightDispatcher.setFocusRequest, though this might have other ramifications. Also, focus events are coming in out of order now. robi.khan@eng 1998-06-24 This bug does not occur in 1.1.7 (but does in 1.1.6/1.2) since the fix that caused it (4111728) was not migrated to 1.1.7. After talking with Jeff Dunn, and Jerome Dochez on the Java Plug In team, we've decided the best thing to do is handle the original problem in the Java Plug In code. Jeff's fix was to synthesize window activation events if a component somehow managed to get the focus but was in an inactive window. This was to workaround the problem that EmbeddedFrame's don't fire activation events-- which makes sense since they are child windows and never can be activated. That fix also changed things to post FOCUS_GAINED events to lightweight components instead of dispatching them directly, which caused Oracle's problem. I suggested we have the Plug-In post activation events on behalf of the EmbeddedFrame, since it is the only piece of code that knows when it's appropriate to do so. Jerome has coded this up on his mahcine and it seems to work fine. There is no need to post FOCUS_GAINED events to lightweight children, so that part of the fix isn't needed. The new AWTEventListener functionality in 1.2 addresses a concern that the (now defunct) EventQueueListener's wouldn't see lightweight focus events that are dispatched rather than posted. AWTEventListener sees all dispatched events now. 1.1.7/1.2fcs will thus require a new version of the Plug-in to work properly. Fix 4111728 will have to be removed from 1.2 as well. robi.khan@eng 1998-08-01 Closing this as not-a-bug in 1.1.7 since the offending code is not present. Fred removed the code in 1.2 as part of his fix for 4164270/4164892. robi.khan@eng 1998-08-13
01-08-1998