FeaturesPluginsDocs & SupportCommunityPartners

EventQueue using.

This document describes a way of using EventQueue by Jemmy. For more information see JavaTM 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.


    Companion
    Projects:
    MySQL Database Server   Open JDK: an Open SourceJDK   GlassFish Community: an Open Source Application Server    Mobile & Embedded Community    Open Solaris   java.net - The Source for Java Technology Collaboration   Open ESB - The Open Enterprise Service Bus Powered by