EventQueue using.
This document describes a way of using EventQueue by Jemmy.
For more information see Java
TM documentation for
java.awt.EventQueue class.
Description
EventQueue replacing
Using public
java.awt.EventQueue API Jemmy installs
own queue implementation. In fact, the only method which is used from
a queue implementation pushed into queues stack is
dispatchEvent(AWTEvent).
This method is invoked to dispatch an event
after the event went
through the queue, so there is no way for Jemmy to do something
before
an event placed into the queue. Fortunately
dispatchEvent(AWTEvent)
overriding gives enough power.
Technical problem
Generally, problem appears for dynamically loaded components.
Most of GUI operations are compound. Simple mouse click on the component center,
for example, contains some steps - coordinates calculation, mouse moving,
pressing and releasing. If component is being moved or resized during this
operation, coordinates may became obsolete.
The problems appears more often for components containing data, such as
trees, lists, tables and so on when some actions required for component contents.
(For example see
26251 issue).
Let me remind what the steps are when test is trying to select a node in a tree:
converts path to the row index by JTree.getRowForPath() method.
gets point for mouse click by JTree.getPathBounds(TreePath) method.
makes mouse click on that point. Which consists in
- mouse moving
- mouse pressing
- mouse releasing
Pre-event steps
First thing to do in order to make such compound operations stable, is performing
all preliminary steps when nothing is happened with the component. It is also
better to put event in the queue right after preparation.
This does not require new event queue registration - it can be done
either by queue locking (by
org.netbeans.jemmy.QueueTool.lock()/unlock() methods)
or by executing all preliminary steps from one Runnable executed through the queue
(by
org.netbeans.jemmy.QueueTool.invoke*(*) method).
Event shorcutting
Even if click coordinates were calculated and events have been posted
simultaneously (in terms of the event queue), some time passes
until component actually receives the events. During this time component
contents may be changed (or component may be moved/resized).
The approach used in jemmy consists in dispatching events directly to
the component - ahead of all events staying in the event queue.
Events are dispatched right from Runnable executed through the queue
by public method of the Jemmy's queue implementation which simply
invokes
super.dispatchEvent(AWTEvent).
Where it can not be used
Shortcut approach can not be used for any action which requires component
reaction between the steps (i.e. redrawing, new components displaying and so on),
because most often component reacts to user input by posting some new events into
the event queue.
For example, combobox item selecting could not be performed as one action, because
combobox processes mouse click and posts some events which will show a list,
then list processes mouse click and generate new events again.
The approach cannot also be used if it's wanted for operations to have some
visual effect. For example, if someone wants to see buttons pressed and released,
button pushing cannot be performed as one action.
Event dispatching
However approach described above showes good stability, the way events
dispatched during such test working is very much different from the way
events dispatched during "regular" application using, so, it might not be
acceptable sometimes.
Thus, there is a possibility to dispatch event "natural" way. In this mode
events are posted just the same as they are posted during manual application using -
in the tail of the queue.
Implementation
Event shortcutting mode.
The way to dispatch event (shorcutting/dispatching) is defined by Jemmy dispatching model:
//shortcut events
JemmyProperties.setCurrentDispatchingModel(
JemmyProperties.getCurrentDispatchingModel() |
JemmyProperties.SHORTCUT_MODEL_MASK);
//dispatch events
JemmyProperties.setCurrentDispatchingModel(
JemmyProperties.getCurrentDispatchingModel() -
JemmyProperties.getCurrentDispatchingModel() &
JemmyProperties.SHORTCUT_MODEL_MASK);
This mode can also been set by
jemmy.shortcut_events system property.
EventQueue subclass.
EventQueue subclass (named
JemmyQueue) is implemented as private inner class in QueueTool,
so it cannot be used directly.
QueueTool
QueueTool has some new method:
public static void postEvent(AWTEvent event)
Simply posts an event.
public static void shortcutEvent(AWTEvent event)
Shortcuts an event using
JemmyQueue
public static void processEvent(AWTEvent event)
Shortcuts event if
((JemmyProperties.getCurrentDispatchingModel() & JemmyProperties.SHORTCUT_MODEL_MASK) != 0)
and if executed in the dispatch thread.
Otherwise posts event.
EventDriver
org.netbeans.jemmy.drivers.input.EventDriver simply uses
QueueTool.processEvent(AWTEvent) method.
100% stable code.
Using event shorcutting it's possible to create 100% stable code for
most of the operations. For example take a look on
org.netbeans.jemmy.drivers.menus.QueueJMenuDriver
sources. One menu pushing action consist in:
1. find (not wait!) next JPopupMenu
2. check that popup is showing
3. find next menuitem on the popup
4. check that menuitem is showing
5. shortcut necessary events (press, enter, or release)
Drivers tries to perform this action until success or until timeout expired.
Supposing timeout is big enough, this algorithm is 100% stable even for
dynamically loaded menus.
Unfortunately, it's not always possible to create 100% stables code.
For example, it's impossible to do for a dynamically loaded component
which lies on scroll pane, because scrolling can not be performed in one
queue action.
Difference with previous version in posting events model.
None of facts below makes any essential difference. Leastways, no effect
was noticed.
Even in event posting mode, events are now posted
slightly differently from how they were posted in the previous Jemmy version.
Previously, for each event
java.lang.Runnable instance was created and
executed by
EventQueue.invokeAndWait(Runnable) method.
This Runnable passes the event directly to a
component. This is not the same as just posting an event into the queue
because of:
1.
java.awt.event.InvocationEvent (which is created as
result of
invokeAndWait) has higher priority
than
java.awt.event.ComponentEvent, so mouse events which were
posted by jemmy went through the queue faster.
2. Not all events put in the queue tail makes the whole way
through the queue. Some of them night be cut by system EventQueue.
For example, events which is going to the components not laying
on the topmost modal dialog are just ignored.
Posting model which is using now, is much closer to the "natural" event
posting because now any event just posted into the queue as it is.
Future possible enhancements
Events filtering
Now during events shortcutting, all events are posted directly to the component.
But, in the real life, some of that events might be filtered by EventQueue
(as described in 2 above). It would be great to understand what events are filtered
by EventQueue and do the same job for our events.
Robot synchronization
This is not connected directly to the queue replacing, but the technique
can be used to ensure that events
generated by robot operations reached the head of the queue. There is
no other way to know that Robot events have been generated and posted.