JDK-4292099 : AWT Event delivery to processEvent
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.1.1,1.3.0
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: solaris_7,windows_nt
  • CPU: x86,sparc
  • Submitted: 1999-11-16
  • Updated: 2001-06-28
  • Resolved: 2001-06-19
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
1.4.0 beta2Fixed
Related Reports
Duplicate :  
Relates :  
Description
Certain AWT events cannot be delivered to the processEvent method of
components that are not in the java.awt package.  While this is largely
the same problem noted in 4061850, this report is more comprehensive and
should supercede that older one.

These event classes in question are

	ActionEvent
	AdjustmentEvent
	ItemEvent
	TextEvent

A small app exists at /home/dherron/work/4061850 that illustrates the
problem and certain workaround possibilities.

The effect is to make it harder to implement lightweight components, and
it leads to possible application performance problems.

By "cannot be delivered", what I mean is

 0- You have a component class, having it enableEvents for the appropriate
   mask, set up a processEvent method that recognizes the xxxEvent class
   and forwards the event to processXXXEvent, as is usual.  I used the
   java.awt.Button source code as a pattern to follow.
 1- You create a new xxxEvent object.
 2- Post this event into the system event queue.
 3- You expect this event to be delivered to processEvent and processXXXEvent,
   that will then forward the event to any xxxEventListener objects.

According to the javadoc:

  protected final void enableEvents(long eventsToEnable)

    Enables the events defined by the specified event mask
    parameter to be delivered to this component. 

    Event types are automatically enabled when a listener for
    that event type is added to the component. 

    This method only needs to be invoked by subclasses of Component
    which desire to have the specified event types delivered to processEvent
    regardless of whether or not a listener is registered.

Hence you would expect the following to deliver ActionEvents to the
components processEvent method:

  class btn extends Component {
	public btn() {
		...
		enableEvents(AWTEvent.ACTION_EVENT_MASK);
                addMouseListener( new MouseAdapter() {
                public void mouseClicked(MouseEvent e) {
                    ActionEvent ae = new ActionEvent(e.getSource(),
					 ActionEvent.ACTION_PERFORMED,
					 getActionCommand());
                    Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ae);
                }
                });
	}

        public synchronized void addActionListener(ActionListener l) {
	    if (l == null) {
	      return;
	    }
	    enableEvents(AWTEvent.ACTION_EVENT_MASK);
	    actionListener = AWTEventMulticaster.add(actionListener, l);
	}
  }

This prevents a goal in event handling stated at
http://java.sun.com/docs/books/tutorial/uiswing/overview/event.html

	    Important:  The code in event handlers should execute very quickly!
	    Otherwise, your program's perceived performance will be poor.
	    If you need to perform some lengthy operation as the result
	    of an event, do it by starting up another thread (or somehow
	    sending a request to another thread)

That is, the best workaround for this problem is to implement the mouseListener
as follows:


  class btn extends Component {
	public btn() {
		...
		enableEvents(AWTEvent.ACTION_EVENT_MASK);
                addMouseListener( new MouseAdapter() {
                public void mouseClicked(MouseEvent e) {
                    ActionEvent ae = new ActionEvent(e.getSource(),
					 ActionEvent.ACTION_PERFORMED,
					 getActionCommand());
                    processEvent(ae);
                }
                });
	}
	...
  }

The effect of calling processEvent directly is that you extend the
amount of time that the MouseEvent delivery takes.  It is not known
the amount of time delivery of the ActionEvent will take, and chaining
together event deliveries in this way can lead to problems (say, if one
or more of the listeners for the ActionEvent then chain off other event
dispatches in the same way rather than using the event queue).

It is better that the design goal (event handlers execute very quickly)
be adhered to and supported by the toolkit.



The life cycle of an event is as follows:

  generate event

  post it to the event queue

  AWT event processing loop takes it off the queue and dispatches
  it to the component.

  it arrives at <component>.dispatchEvent and propogates to
  processEvent

  processEvent looks at the event class and propogates it to
  the appropriate processXXEvent

  processXXEvent checks the listeners and, if any, calls the approprite
  listener delivery method

This problem breaks that life cycle at the dispatchEvent location.

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: generic FIXED IN: merlin-beta2 INTEGRATED IN: merlin-beta2 VERIFIED IN: merlin-beta2
14-06-2004

WORK AROUND I know of four workarounds: - call the listeners directly to cause the event to be delivered. - call processEvent directly to cause the event to be delivered. - when posting the event, use an invalid event id. - use some other event class and an event id greater than RESERVED_ID_MAX These workarounds work, but are not desirable for the following reasons: As noted it is highly desirable for each event delivery to take a short amount of time to happen. With the first two workarounds you are increasing the delivery time of the event by chaining together the processing of two separate events. In the third workaround, using an invalid event id for the event class, is most interesting partly because it illustrates where the problem in the AWT source resides. It is not desirable because then the event is improperly formed and if some code is looking at the event ID to decide what to do it won't understand the event. The fourth workaround is less desirable because if the proper event class is already there (say, you are implementing a lightweight button, LWButton, in that case ActionEvent is what you want to use) it is that class which should be used.
11-06-2004

SUGGESTED FIX The four event classes are ones for which Component.java does not recognize their event masks, or provide for them in the default processEvent method. One of the workarounds is most interesting and illustrative of where the problem is. In dispatchEventImpl is the following: if (eventEnabled(e)) { processEvent(e); } So if eventEnabled were to return true, then processEvent would be called and the bug would be solved. So what is happening in eventEnabled? We see two things. First that it doesn't have any tests for the four event classes (rather, their event masks) that this report concerns with. Second, if the event ID is greater than RESERVED_ID_MAX it always indicates true. So by posting, say, an ActionEvent with id RESERVED_ID_MAX+1, when the event gets into eventEnabled, the tests fall into the if at the bottom that makes it return true. This tells us, also, an interesting solution. Add these lines to Component.eventEnabled: case ActionEvent.ACTION_PERFORMED: if ((eventMask & AWTEvent.ACTION_EVENT_MASK) != 0) { return true; } break; case TextEvent.TEXT_VALUE_CHANGED: case TextSelectionEvent.TEXT_SELECTION_CHANGED: if ((eventMask & AWTEvent.TEXT_EVENT_MASK) != 0) { return true; } break; case ItemEvent.ITEM_STATE_CHANGED: if ((eventMask & AWTEvent.ITEM_EVENT_MASK) != 0) { return true; } case AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED: if ((eventMask & AWTEvent.ADJUSTMENT_EVENT_MASK) != 0) { return true; } The tests here are slightly different than the ones already in eventEnabled since they do not check if there are xxxListener objects registered in the component. But then this suggested code can't because it would be in the wrong class. The existing components (Button, List, TextComponent, Scrollbar, etc) are able, since they are part of java.awt, to implement the eventEnabled method themselves and do appropriate tests there. For a component not in the package java.awt it does not have this ability. Another solution is to allow components not in java.awt to implement the eventEnabled method, and the eventEnabled methods would look a heck of a lot like the ones in Button.java, List.java and the like. But for the cryptic comment in the source about removing this stuff when event filtering is done at a lower level, this might actually be the preferred solution. However that cryptic comment leads me to believe that a redesign is planned at some point that would greatly revamp event delivery. And, in any case, it might be a performance hit to have eventEnabled be spread around like that.
11-06-2004

EVALUATION Commit to fix in Merlin (SQE yellow bug). eric.hawkes@eng 2000-11-09 Add cases to Component.eventTypeEnabled() to ensure that the events ActionEvent, TextEvent, ItemEvent, and AdjustmentEvent are delivered to processEvent. See Suggested Fix. mike.bronson@eng 2001-06-06
06-06-2001