Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Thursday, February 18, 2010

JTextArea vs. Tab focus cycle

When inside a JTextArea, the Tab key inserts a tab character instead of going to the next field. The reason is obvious - but in several situations it is not desirable, in particular when it would not make sense to insert tab characters, for example in a text area meant for editing keywords.

Lets just spill the solution right away:
JTextArea text = new JTextArea();
text.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null);
text.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null);

There is also a built-in solution, which is that Ctrl-[Shift-]Tab still works. Actually, those keys always work. Those keys are also the solution when you suddenly find yourself focus-stuck within a JTable. I first thought that a JTable constituted a a "focus cycle root", but it doesn't - it is just the same effect as with the JTextArea problem here outlined (and can be "fixed" the same way).

However, Ctrl-Tag does not quite give the feeling one typically is after, and in particular not when one use the JTextArea more as a solution to text that typically is less than a line big, but can become multiline. Such a field thus looks like a JTextField, but the Tab character suddenly have a completely different meaning.

I Googled a lot, and there are lots of solutions (or rather hacks), but didn't find the above solution, which seems to be the most straightforward after Java 1.5. The huge heap of questions and solutions should, IMHO, give a hint to the Swing team that a specific method for a JTextArea to not suck up Tab events should be made available.

The null means that it will take the focus keys from its parent.

Thursday, February 11, 2010

Anonymous class implementing interface

Why is this not allowed:
JPanel panel = new JPanel(...) implements Scrollable {
    ...
};

I've ended up trying to write something like that several times - and each time I start to wonder what is wrong! A problem is of course that you could not directly access the methods of the implemented interface, but you could still have instanceof'ed and cast'ed it. Some interfaces are also just used for marking, e.g. Cloneable.

It is allowed with a local class:
class ScrollableJPanel extends JPanel implements Scrollable {
    ScrollableJPanel() {
        super(...);
    }
    ...
}
JPanel panel = new ScrollableJPanel();
The restriction seems arbitrary.

PS: Oh my god Blogger sucks. I have GOT to get away from this crappy blogging software. How is it possible to not have made any progress the last 5 years?!

Tuesday, March 3, 2009

AWT Swing Event Pumping and Targeting

I'm currently learning Swing, and thus AWT. In the post "Debug Listeners on AWT/Swing components", I wrote a little piece of code that can help with understanding what events are fired on a component.

This is a further dive into the lower level elements of the event system of AWT/Swing. I wrote this as research notes for myself, but have then tried to edit it into something more readable, hoping that a wider audience than me alone can get any value from it.

Intended audience: Somewhat experienced developers of the type that likes to know how things really work, and who subscribe to Joel's law of Leaky Abstractions. If you're a AWT/Swing guru that for example know how the current version of AWT handles the AWT 1.0 event model and where exactly in the event dispatch and processing chain the AWTEventListeners are invoked, you probably don't need this. If you've never written some hello world for Swing, it might be too early - but you could skim it nevertheless, to know if it will be interesting for you later.

In the following text, which is a long article more than a blogpost, I ended up covering quite a bit. The main elements are:
  1. Basics of AWTEvents, low-level events and semantic (high-level) events.
  2. Event pumping, Operating System to Java interaction and the transfer of events.
  3. Event dispatching, Event Dispatch Thread ("EDT")
  4. Event processing.
  5. InputEvent (mouse and keyboard) retargeting, Focus subsystem
  6. Some misc topics: Coalescing, AWTEventListeners, Key Bindings, and how semantic events are produced and dispatched (Spring's MVC logic).
Please be advised that there might be places where I haven't understood things correctly! The text is neither supposed to be a complete discussion of all the intricacies of Swing/AWT. However, the text might hopefully, for some level of developers, shed light on some elements of "how it really works". The text is supposed to progress in a somewhat orderly fashion - if it doesn't, just read it a couple of times more! If you have any corrections, ideas for making it easier to understand this stuff, or otherwise have any suggestions, please comment - or send an email to Endre@Stolsvik.com.

The text contains a bunch of stacktraces. These are taken from runs on Java 1.6.0_12. However, these parts of the code don't change radically from version to version, and the overall discussion and even the stacktraces (less the exact line numbers) will probably still be relevant in some years from now.

Basics

Different types of events: The event system comprises two distinct types of events: Low-level events, and Semantic events. The low-level events all extend ComponentEvent, while the semantic events are all others, for example ActionEvent, AdjustmentEvent, ItemEvent, TextEvent and others. The low-level events are direct events concerning the GUI system and represent window-system occurrences or low-level input, like a window being moved or minimized, a key being pressed on the keyboard, the mouse being moved, a mousebutton being clicked. The semantic events, on the other side, pretty much all describe changes of state on Components, like a button being clicked, a menu item being choosen, text being entered into a field. Semantic events are typically constructed and dispatched by the components themselves based on low-level events.

Here's a rather old (Feb 1997) article from Sun: "Java AWT: Delegation Event Model", concerning the change from Java 1.0 event model to the Java 1.1 delegation event model. This model is still the one being employed, and the article is a rather easy read - just decide if the historic parts are worth paying attention to or not. Here's a link to the Swing UI tutorial about these two kinds of events: "Concepts: Low-Level Events and Semantic Events". Here's a link to the event package JavaDoc: Package java.awt.event.

This article will primarily concern the low-level events.

All AWT/Swing events are of supertype AWTEvent: The events fired on the different listeners are extensions of AWTEvent, for example MouseEvent, and the specific subtype of mouse event is communicated by which of the different methods on the MouseListener (or MouseMotionListener or MouseWheelListener) is invoked, for example MouseListener.mouseClicked(MouseEvent e). AWTEvent also has a method getID() that describes which type (e.g. "mouse") and subtype (e.g. "mouse moved") the event is (MouseEvent ids starts at 500, and MOUSE_MOVED is 503). AWTEvent is a subclass of Event, which has a getSource() method. The source always refers to the component "in question": Low-level events originate from the windowing system, and the source is the component that got or will get the Event, and it can thus quite cleanly be thought of as the target. High-level events are typically created by the components themselves, and are thus originating from that component, making "source" correcter. When processing events, the EventDispatchThread, which is discussed later, directly invokes getSource() on the event, and then invokes source.dispatchEvent(event). The specific event classes add event type specific stuff, for example MouseEvent which amongst others has methods getLocationOnScreen() and getButton().

Here's the Sun tutorials index for EventListeners: "Lesson: Writing Event Listeners", which has lots of good information that hopefully will make even more sense after reading this article. In particular the listing in "Listeners Supported by Swing Components" is good for an overview or index over what event listeners are supported by which components. Note how it divides the list into low-level events ("Listeners that All Swing Components Support" - the AWT events) and higher-level semantic event listeners ("Other Listeners that Swing Components Support"). From this page you can get a pdf of Chapter 9 of the book "Mastering the JFC: AWT, Volume 1", which is a 2001 book but which still is relevant.

From the Operating System to the MouseListener

Native Event Loop: The Toolkit implementation and its corresponding event pumping thread is the native connection to the underlying operating system, and is thus specific for each operating system. Inside the Toolkit thread event loop, extension classes of AWTEvent (e.g. MouseEvent) are instantiated and posted to the EventQueue. The source is set to the appropriate AWT Window: For example, for KeyEvents, this is the active window, while for MouseEvents, it is the window below the mouse. The AWTEvent is "posted" to the java side by some roundabout ways which effectively ends up in an invocation of EventQueue.postEvent(AWTEvent). The specific Toolkit instance along with the toolkit thread forms the native side of the GUI event pumping.

The source code for the native Toolkit implementations aren't available in the standard SDK distribution. Here's a link to WToolkit, the Windows Toolkit implementation. Notice the bunch of native methods. Notice the native method eventLoop() at line 303 and the call to this from the Runnable of the "AWT-Windows" thread at line 289.

Here's a stacktrace of a posting of a MouseEvent.MOUSE_MOVED event from the native Toolkit thread, named "AWT-Windows". Notice how it doesn't post it directly onto the EventQueue, but rather onto a "intermediate storage point" called PostEventQueue:

 Daemon Thread [AWT-Windows]
  sun.awt.PostEventQueue.postEvent(java.awt.AWTEvent) line: 2083
  sun.awt.SunToolkit.postEvent(sun.awt.AppContext, java.awt.AWTEvent) line: 591
  sun.awt.windows.WFramePeer(sun.awt.windows.WComponentPeer).postEvent(java.awt.AWTEvent) line: 722
  sun.awt.windows.WToolkit.eventLoop() line: not available [native method] [local variables unavailable]
  sun.awt.windows.WToolkit.run() line: 291 java.lang.Thread.run() line: 619

Java Event Loop: The EventDispatchThread ("EDT") is a thread that basically repeatedly invokes EventDispatchThread.pumpOneEventForFilters(int id) in a loop. This again invokes EventQueue.dispatchEvent(AWTEvent e). The EventQueue and EventDispatchThread together forms the java side of the event pumping. Upon dispatch, the EventQueue does checks what type of Event is being handled. ActiveEvents has a method dispatch() which is invoked directly. If the source of the AWTEvent is a Component, this component has its dispatchEvent(AWTEvent) invoked with the event as the argument.

The EventQueue gets events posted either from the native Toolkit thread or from the application code ("userland"). EventQueue.invokeLater(Runnable) and invokeAndWait(Runnable) posts an event to the EventQueue. Such events will be of type InvocationEvent (which is an ActiveEvent). It is also possible to directly post events onto the queue, as such: Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event), but getting the EventQueue might be denied by the call to System.getSecurityManager().checkAwtEventQueueAccess(). Note that a lot of event firing, particularly for the higher-level events (the Semantic events), doesn't go through the EventQueue. For example, if a JButton is clicked, the generated ActionEvent is never posted to the EventQueue, but instead fired on the button's ActionListeners directly ("directly" is relative; The event is fired through or rather by Swing's Model-View-Controller paradigm, but still not through the EventQueue - more on this later).

Continuing on the MouseEvent.MOUSE_MOVED example started above: The EventQueue is notify'ed, leading to a flush of events from the PostEventQueue into the EventQueue. The thread here is the java side, the EventDispatchThread, named "AWT-EventQueue-0" (The zero at the end of the name is increased if this thread crashes, in which case a new one is created when the next event is posted by the Toolkit, and also increased for modal dialogs):

 Thread [AWT-EventQueue-0]
  java.awt.EventQueue.postEvent(java.awt.AWTEvent, int) line: 244
  java.awt.EventQueue.postEventPrivate(java.awt.AWTEvent) line: 202
  java.awt.EventQueue.postEvent(java.awt.AWTEvent) line: 175
  sun.awt.PostEventQueue.flush() line: 2072
  sun.awt.SunToolkit.flushPendingEvents() line: 626
  java.awt.EventQueue.getNextEvent() line: 465
  java.awt.EventDispatchThread.pumpOneEventForFilters(int) line: 236
  java.awt.EventDispatchThread.pumpEventsForFilter(int, java.awt.Conditional, java.awt.EventFilter) line: 184
  java.awt.EventDispatchThread.pumpEventsForHierarchy(int, java.awt.Conditional, java.awt.Component) line: 174
  java.awt.EventDispatchThread.pumpEvents(int, java.awt.Conditional) line: 169
  java.awt.EventDispatchThread.pumpEvents(java.awt.Conditional) line: 161
  java.awt.EventDispatchThread.run() line: 122

Two-thread event pump: Notice how there are thus two completely separate threads doing event pumping: One running on the native side, the Toolkit thread, pumping events from the native operating system "over to java" via posting to an intermediate PostEventQueue class, and then notifying the java side via the EventQueue. On the java side, we have the EventDispatchThread that waits on the EventQueue. When it gets a notify, it first flushes any new events from the native side into the EventQueue proper, and then pumps from the EventQueue.

Dispatching on the AWT and Swing Components: The main dispatching method on to the AWT/Swing component side is Component.dispatchEvent(AWTEvent e), which is final, but invokes Component.dispatchEventImpl(AWTEvent) which is package-private and is overridden by Container and furthermore by Window. Handled events in these overrides include ComponentEvent.COMPONENT_RESIZED for Windows, in which case it invalidates and validates before passing on to super, and resize and move events for Container, in which case it produces HierarchyEvent.ANCESTOR_[RESIZED|MOVED] for its children, after it has invoked super. However, Container also takes care of all MouseEvents, doing retargeting and redispatch, which we will come back to.

The Component.dispatchEventImpl(AWTEvent) is a rather long method that handles different aspects of different events, divided into steps. It takes care of a lot of special cases, both because of special requirements for some types of events, for the KeyboardFocusManager and lightweight component retargeting for events handled by focus, and for flexibility, but also because of lots of cruft in the event handling accumulated throughout the years (In particular this goes for the Java 1.0 AWT style of event handling (explained in the article "Java AWT: Delegation Event Model" referenced above) whereby one had to subclass a component to get events, which then were delivered by the Component's "self-invocation" of for example the overridden Component.mouseDown(Event e)). The Component.dispatchEventImpl(e) are nicely commented inline, clearly showing each stage of the dispatch. Unless some special case kicks in (of which there are many, in particular input event retargeting, described shortly), the method processEvent(e) is eventually invoked in step "6. Deliver event for normal processing", but only if Component.eventEnabled(AWTEvent e) returns true. This method returns true if either the event-type specific listener field is non-null, or if the eventMask, set by enableEvents(long), says so. The processing is described shortly.

InputEvents (re)targeting: The (currently) two types of InputEvents have distinct targeting logic, meaning which Component's Listeners shall be invoked. MouseEvents goes to the most specific ("deepest") child component residing under the mouse, while KeyEvents are sent to the focused window, routed using the KeyboardFocusManager to the focused component in this window.

MouseEvents targeting: Container overrides dispatchEventImpl(AWTEvent). Remember that Window is a Container. If the Container in question is a heavyweight component (i.e. it is not an instanceof LightweightPeer), this override takes care of finding the target for MouseEvents, attempting via an instance of the class LightweightDispatcher to forward/distribute MouseEvents to the most specific ("deepest") child, whether this is a heavyweight AWT Component, or a lightweight JComponent. These days this class has an incorrect name, since it also is responsible for retargeting MouseEvents to any heavyweight Components. (The class is package private, residing in Container.java).

To find the deepest component, Container.getMouseEventTarget(x, y ...) is invoked, which goes through each child component, finding the one that returns true for Component.contains(x, y). If the child is a Container, it recurses by invoking getMouseEventTarget(x - comp.x, y - comp.y ...) on the child (hence upholding the "illusion" that 0,0 is the local top left corner for all component). Since JComponents extends from Container, this method also checks itself after the recurse. The check mentioned is whether it wants any MouseEvents by testing whether the eventMask says so (mentioned above), or if any of the three types of mouse listeners are set (Mouse, MouseMotion, MouseWheel). The net effect is that the LightweightDispatcher thus finds the deepest Component which the mouse is currently over and which wants MouseEvents, retargets the event by making a new one, dispatches the new event instance directly, and consumes the original event instance. Or it finds that there are no such component, either because there physically aren't any components there (the window background is showing), or because the component don't want MouseEvents (for example a standard JLabel doesn't), in which case the LightweightDispatcher won't do anything (it won't consume the event), and the event will be dispatched as normal (on itself, the native Container, typically a Window).

LightweightDispatcher also tracks mouse enter and exits over lightweight components as the native system doesn't know about them and thus cannot make enter/exit events for them either. Also, if we're dragging, it hooks an AWTEventListener on the Toolkit (as mentioned below), so that it gets all further MouseEvents even though we drag across different boundaries of lightweight and heavyweight components.

If the original event is consumed, the LightweigthDispatcher dispatchEvent returns true, and the Container.dispatchEvent returns. If it doesn't consume the event (hence, it was no MouseEvent, or it was a MouseEvent targetted at the Container itself), it returns false, and the Container.dispatchEvents continues by invoking super.dispatchEventImpl(AWTEvent e), and hence normal dispatch ensues.

KeyEvent targeting / Focus Subsystem: As briefly mentioned, Component.dispatchEventImpl(e) also handles keyboard targeting: KeyboardFocusManager's static method retargetFocusEvent() is invoked, apparently to handle actual FOCUS_GAINED and FOCUS_LOST events. Then, the KeyboardFocusManager is statically asked for the current KeyboardFocusManager (I am not sure if it really is possible to entirely roll your own KFM, as the abstract class KFM specifically states several places that it depends on the code in DefaultKeyboardFocusManager), and dispatchEvent(e) is invoked on that. If this invocation returns true, the event was dispatched, and we are finished. It is possible to register KeyEventDispatcher instances on the KeyboardFocusManager, which can consume the event - the focused component is already established at that point, being set in the KeyEvent (getSource(), or rather getComponent() since it is a ComponentEvent - read towards the top, "All AWT/Swing events are of supertype AWTEvent"). Here's the Sun Java tutorial: "How to use the Focus Subsystem". For a in-depth description of the focus subsystem, read the article The AWT Focus Subsystem, referenced from the Component class and in particular its processKeyEvent method. This subsystem is somewhat complex, including logic about multiple focus cycle roots and how the different components are notified about focus gained and lost events. An interesting note is that if you end up in a more "inner" focus cycle root, there are by default no keypresses that can lift you out to the outer root again.

Note that for JComponents, there is another way to hook Actions to specific KeyStrokes, namely the InputMap/ActionMap system, which is described below.

Event processing on Component: As mentioned above, Component.processEvent(AWTEvent e) is eventually invoked. It has a bunch of "downstream" methods: process[Component|Focus|Key|Mouse[Motion|Wheel]|Input|Hierarchy[Bounds]]Event. All these methods can be overridden in subclasses, thus providing interesting (and non-trivial) hook-points. If you do override them, remember enableEvents(bitfield), described shortly. It is these downstream methods, for example processMouseEvent, that eventually invokes the listeners, for example MouseListener.

For any type of event, there can be several EventListeners on a Component. In the Component class, there is a sole field for each type, e.g. keyListener. Nullness of this field is used as indicator: If it is null, there is no listeners for that event type. If it is non-null, there is at least one. If there is one listener, it will be the actual listner. If there are several listener, the field will be an AWTEventMulticaster instance, which is a somewhat clever way of simply daisy-chaining a set of listeners of the same type into a string of listeners whose event fire methods all will be invoked when the AWTEventMulticaster's corresponding fire-method is invoked. AWTEventMulticaster obviously implements all types of AWT EventListeners. The order in which listeners of a specific type is invoked is indeterminate, thus if important, you must handle this in some way yourself.

Events are not delivered to a Component unless there are at least one listener for this event, or if enableEvents(long eventsToEnable) is invoked (in which case processEvent(e) is invoked even though there are no listeners for that type). The long is a bit-mask, composed by ORing together the constants in AWTEvent. The process[Type]Event does the null-check on the corresponding typeListener field, returning immediately if null. If non-null, it invokes the correct method on the TypeListener based on a switch on event.getID(). All process[*]Event methods are protected and non-final, hence overridable for subclasses.

MouseEvent.mouseClicked: Here is a stacktrace for invocation of mouseClicked(e) on a MouseListener installed on a JLabel when I clicked the mouse. The stacktrace begins in the EventQueue where an event is pumped. It is dispatched on the Window instance where it goes to the Window and Container specific overrides of dispatchEventImpl. It is a MouseEvent, so it goes through the LightweightDispatcher where this MouseEvent is retargeted (a new MouseEvent is created, the old is consumed). The LightweigthDispatcher directly redispatches the new MouseEvent on the JLabel instance where it is processed. Finally, it ends up in the ExampleMouseListener as an invocation on its mouseClicked(e) method. (In this example, the JComponent also intervenes to handle "Autoscroll". This is a feature that enables moving of a scrollpane by dragging the content: If you setAutoscrolls(true), synthetic MouseDragged events will be made if you click inside the component and then drag the mouse, even though you drag it outside the component. The override of the processMouseEvent is to stop this feature when you release the mouse button.). Here's Sun's tutorial on MouseListeners: "How to write a Mouse Listener". The thread running is, as always for Java GUI stuff, the EventDispatchThread.

 Thread [AWT-EventQueue-0]
  com.example.ExampleMouseListener.mouseClicked(...)
  java.awt.Component.processMouseEvent(Component.java:6219)
  javax.swing.JComponent.processMouseEvent(JComponent.java:3265)
  java.awt.Component.processEvent(Component.java:5981)
  java.awt.Container.processEvent(Container.java:2041)
  java.awt.Component.dispatchEventImpl(Component.java:4583)
  java.awt.Container.dispatchEventImpl(Container.java:2099)
  java.awt.Component.dispatchEvent(Component.java:4413)
     ^^ This is a redispatch of a new MouseEvent, now on the JLabel, while the old event will be consumed.
  java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4556)
     ^^ It has now found the target Component: The JLabel
  java.awt.LightweightDispatcher.processMouseEvent(Container.java:4229)
  java.awt.LightweightDispatcher.dispatchEvent(Container.java:4150)
     ^^ The retargeting of the MouseEvent begins, by the LightweightDispatcher held by the Window instance.
  java.awt.Container.dispatchEventImpl(Container.java:2085)
  java.awt.Window.dispatchEventImpl(Window.java:2475)
  java.awt.Component.dispatchEvent(Component.java:4413)
     ^^ The initial dispatch, on the Window that holds the JLabel.
  java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
  java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)    <-- "EDT pump" references!
  java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
  java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
  java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
  java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
  java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

KeyEvent.keyTyped: To contrast, here's a stack trace for invocation of keyTyped(e) on a KeyListener installed on a JButton. I made the button focused (by clicking on it, or by tabbing to it), then hit a key. The event is processed the same up to the Container.dispatchEventImpl, where the event is not processed, and instead goes further to Component.dispatchEventImpl. Here, the KeyboardFocusManager kicks in since it is a KeyEvent, and does its magic focusing logic and where the KeyEvent is retargeted (a new KeyEvent is created, the old is consumed). The KeyboardFocusManager directly redispatches the new KeyEvent on the JButton, where it is processed. Finally, it ends up in the ExampleKeyListener as an invocation on its keyTyped(e) method. Here's Sun's tutorial on KeyListeners: "How to write a Key Listener".

 Thread [AWT-EventQueue-0]
  com.example.ExampleKeyListener.keyTyped(...)
  java.awt.Component.processKeyEvent(Component.java:6171)
  javax.swing.JComponent.processKeyEvent(JComponent.java:2799)
  java.awt.Component.processEvent(Component.java:5993)
  java.awt.Container.processEvent(Container.java:2041)
  java.awt.Component.dispatchEventImpl(Component.java:4583)
  java.awt.Container.dispatchEventImpl(Container.java:2099)
  java.awt.Component.dispatchEvent(Component.java:4413)
     ^^ This is a redispatch of a new KeyEvent, now on the JButton, while the old will be consumed.
  java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1848)
     ^^ It has now found the target Component: The JButton
  java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:704)
  java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:969)
  java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:841)
  java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:668)
     ^^ The retargeting of the KeyEvent begins, by the global KeyboardFocusManager
  java.awt.Component.dispatchEventImpl(Component.java:4455)
  java.awt.Container.dispatchEventImpl(Container.java:2099)
  java.awt.Window.dispatchEventImpl(Window.java:2475)
  java.awt.Component.dispatchEvent(Component.java:4413)
     ^^ The initial dispatch, on the Window that holds the JButton.
  java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
     ["EDT pump"]


Misc topics

Coalescing: This is a mechanism whereby a bunch of e.g. mouse-move events are compressed into one. This is done by checking whether the event currently-being-posted-to-the-Event-Queue could be collapsed into an already-posted-but-not-yet-processed event. Earlier, this was a general system whereby the source ("target") component would possibly go through the entire eventqueue looking for same-type events, checking whether it could collapse them, but this has been deemed too slow. The EventQueue.coalesceOtherEvent method that takes care of this legacy has the following comment: "Should avoid of calling this method by any means as it's working time is dependant on EQ length. In the wors case this method alone can slow down the entire application 10 times by stalling the Event processing. Only here by backward compatibility reasons." The present system uses the field Component.eventCache, where five specific event types can be handled: PaintEvent.PAINT, PaintEvent.UPDATE, MouseEvent.MOUSE_MOVED and MouseEvent.MOUSE_DRAGGED, in addition to sun.awt.PeerEvent (which I don't know what is) which apparently handles coalescing itself (it also handles dispatching itself - it is an ActiveEvent, specifically an extension of InvocationEvent). The coalescing is as dumb (and fast) as possible: mouse events are coalesced by keeping the latter event, while the paint events are coelesced by checking whether one or the other "update rect" is contained in the opposite, in which case it returns the containing, or else it doesn't coalesce.

AWTEventListener, "Event spying": On the Toolkit, one may install a "global listener" to spy on all low-level events that are dispatched from the EventQueue onto some Component: Toolkit.getDefaultToolkit().addAWTEventListener(listener, long eventMask), where the eventMask is the same bit-mask described Component.enableEvents(...) above. Adding an AWTEventListener might be denined by the call to System.getSecurityManager().checkPermission(SecurityConstants.ALL_AWT_EVENTS_PERMISSION).

This is a stacktrace when such an AWTEventListener is invoked, also on the EventDispatchThread, illustrating both the EventQueue and the run through the dispatching on the Window, Container, Component. The invocation of AWTEventListeners are done early in the Component.dispatchEventImpl, at step "2. Allow the Toolkit to pass this to AWTEventListeners", which makes it possible for a AWTEventListener to consume the event early and thereby stop the dispatch process from going further.

 Thread [AWT-EventQueue-0]
  com.example.ExampleAWTEventListener.eventDispatched(...)
  java.awt.Toolkit$SelectiveAWTEventListener.eventDispatched(Toolkit.java:2353)
  java.awt.Toolkit$ToolkitEventMulticaster.eventDispatched(Toolkit.java:2244)
  java.awt.Toolkit.notifyAWTEventListeners(Toolkit.java:2203)
     ^^ The Toolkit is invoked to pass the event through the AWTEventListeners.
  java.awt.Component.dispatchEventImpl(Component.java:4481)
  java.awt.Container.dispatchEventImpl(Container.java:2099)
  java.awt.Window.dispatchEventImpl(Window.java:2475)
  java.awt.Component.dispatchEvent(Component.java:4413)
  java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
     ["EDT pump"]

Key Bindings / Keyboard handling by InputMap and ActionMap: Swing components, JComponents, have one more way of handling KeyEvents: The InputMap and ActionMap. The InputMap defines a Map between KeyStrokes and "actionMapKeys" which (typically) are Strings that describes what this key does, while the ActionMap defines a Map between those actionMapKeys and some Action. Note that the actionMapKey arguement type is Object, but it is custom to use Strings so that these Maps become "self documenting".

Read the JavaDoc of the static methods on KeyStroke carefully; They are a little devious! The whole key handling system reeks of legacy. The problem spots revolve around the fact that for KEY_TYPED events, the KeyEvent instance returns the typed character in the method getKeyChar(), while for KEY_PRESSED and KEY_RELEASED events, one needs to use the getKeyCode() (which returns a KeyEvent.VK_[key] constant). This also makes for interesting times when using the getKeyStroke methods - be sure to read the JavaDocs. In particular, both the methods getKeyStroke(int keyCode, int modifiers) and getKeyStroke(int keyCode, int modifiers, boolean onKeyRelease) return strokes for KEY_PRESSED and KEY_RELEASED (the latter if the boolean is true), meaning that there are no way to get a stroke for KEY_TYPED when using the key codes.

This binding system is the functionality used by mnemonics (picking a menu item from a selected menu by a key, typically this letter is underlined) and accelerators (picking some menu item without navigating the menu, e.g. "Ctrl-F" for File->Open). There is also a trick here for the UI (the Look and Feel): The UI have a separate parent Map that takes care of any Look'n'Feel specific key bindings, and makes it possible to change the look and feel without loosing the application specific bindings.

Here's the JavaDoc from KeyboardManager, a package private helper class for the key bindings system, which I found nicely summing up the keybindings system:

" The KeyboardManager class is used to help dispatch keyboard actions for the WHEN_IN_FOCUSED_WINDOW style actions. Actions with other conditions are handled directly in JComponent.

Here's a description of the symantics of how keyboard dispatching should work atleast as I understand it.


KeyEvents are dispatched to the focused component. The focus manager gets first crack at processing this event. If the focus manager doesn't want it, then the JComponent calls super.processKeyEvent() this allows listeners a chance to process the event.


If none of the listeners "consumes" the event then the keybindings get a shot. This is where things start to get interesting. First, KeyStokes defined with the WHEN_FOCUSED condition get a chance. If none of these want the event, then the component walks though it's parents looked for actions of type WHEN_ANCESTOR_OF_FOCUSED_COMPONENT.


If no one has taken it yet, then it winds up here. We then look for components registered for WHEN_IN_FOCUSED_WINDOW events and fire to them. Note that if none of those are found then we pass the event to the menubars and let them have a crack at it. They're handled differently.


Lastly, we check if we're looking at an internal frame. If we are and no one wanted the event then we move up to the InternalFrame's creator and see if anyone wants the event (and so on and so on).
"

Since this article has come to include a bunch of stack traces to illustrate things, I'll chuck in a couple more to show where the keybindings kick in the event processing. On a window with two buttons, I've bound F2 to JButton A "WHEN_FOCUSED", F3 to the containing JPanel "WHEN_ANCESTOR_OF_FOCUSED_COMPONENT", and finally F4 to the same JButton A "WHEN_IN_FOCUSED_WINDOW". Hitting key F2, F3 and F4 when the button A is focused "hits" on all keys, while if JButton B is focused, F2 doesn't fire, but F3 and F4 still does; F2 doesn't fire since it is not focused, F3 fires since it is bound to the ancestor JPanel of JButton B, while F4 fires since the window that JButton B resides in is focused.

Here's the stacktrace for F2, which is bound to the JButton A "WHEN_FOCUSED", and fires when JButton A has focus:

 Thread [AWT-EventQueue-0]
  com.example.ExampleAction.actionPerformed(...)
  javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1636)
  javax.swing.JComponent.processKeyBinding(JComponent.java:2849)
  javax.swing.JComponent.processKeyBindings(JComponent.java:2884)
      ^^ Checking own bindings
  javax.swing.JComponent.processKeyEvent(JComponent.java:2812)    <-- .. next trace starts here!
  java.awt.Component.processEvent(Component.java:5993)
  java.awt.Container.processEvent(Container.java:2041)
  java.awt.Component.dispatchEventImpl(Component.java:4583)
  java.awt.Container.dispatchEventImpl(Container.java:2099)
  java.awt.Component.dispatchEvent(Component.java:4413)
     ^^ This is a redispatch of a new KeyEvent, now on the JButton, while the old will be consumed.
  java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1848)
     ^^ It has now found the target Component: The JButton
  java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:704)
  java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:969)
  java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:841)
  java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:668)
     ^^ The retargeting of the KeyEvent begins, by the global KeyboardFocusManager
  java.awt.Component.dispatchEventImpl(Component.java:4455)
  java.awt.Container.dispatchEventImpl(Container.java:2099)
  java.awt.Window.dispatchEventImpl(Window.java:2475)
  java.awt.Component.dispatchEvent(Component.java:4413)
     ^^ The initial dispatch, on the Window that holds the JButton.
  java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
     ["EDT pump"]

Here's the stacktrace for F3, which is bound to the JPanel "WHEN_ANCESTOR_OF_FOCUSED_COMPONENT", and fires when either of the buttons (or anything else residing in that JPanel) has focus:

 Thread [AWT-EventQueue-0]
  com.example.ExampleAction.actionPerformed(...)
  javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1636)
  javax.swing.JComponent.processKeyBinding(JComponent.java:2849)
      ^^ This invocation is now on the JPanel and not the JButton anymore
  javax.swing.JComponent.processKeyBindings(JComponent.java:2895)
      ^^ Traversing parents
  javax.swing.JComponent.processKeyEvent(JComponent.java:2812)    <--
     -- same as above

And finally, here's the stacktrace for F4, which is bound to the JButton A "WHEN_IN_FOCUSED_WINDOW", and fires when either of the buttons (or anything else in the entire Window) has focus:

 Thread [AWT-EventQueue-0]
  com.example.ExampleAction.actionPerformed(...)
  javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1636)
  javax.swing.JComponent.processKeyBinding(JComponent.java:2849)
  javax.swing.KeyboardManager.fireBinding(KeyboardManager.java:267)
  javax.swing.KeyboardManager.fireKeyboardAction(KeyboardManager.java:216)
      ^^ Runs through all (showing) components that have registered WHEN_IN_FOCUSED_WINDOW maps.
  javax.swing.JComponent.processKeyBindingsForAllComponents(JComponent.java:2926)
      ^^ This is a static method, invoking the global KeyboardManager
  javax.swing.JComponent.processKeyBindings(JComponent.java:2918)
      ^^ Given up on local component bindings!
  javax.swing.JComponent.processKeyEvent(JComponent.java:2812)    <--
     -- same as above

It is worth noting that even if a key binding is bound to the InputMap WHEN_IN_FOCUSED_WINDOW for some component, the bound action will only fire if the component is showing and enabled. This means that if you have a JTabbedPane, adding a WHEN_IN_FOCUSED_WINDOW KeyBinding to some component on some tab, the action will only fire if this tab is the one showing - i.e. it won't fire if some other tab of that same JTabbedPane is showing. (The JTabbedPane alters the "visible" property of its contained components when changing tabs, which thus changes the showing property: Showing is when you are visible and all grandparents up to and including the window also are visible). This effect is probably what you want (!), but if not, then you'd obviously just do the key binding on the JTabbedPane instead, or even closer to the root Window.

Also, as could be read out of the JavaDoc rip above, if a component consumes the event, it will not percolate further, which most probably is what you'd want. This means that if the focus is on a JButton, and Space is pressed, this event will be consumed by the button to click it, and the event will not fire any of the WHEN_ANCESTOR_OF_FOCUSED_COMPONENT OR WHEN_IN_FOCUSED_WINDOW Actions bound further up in the hierarchy. Also, if a JTextField is focused, all KEY_TYPED events will be consumed in the same fashion, so that what one writes into the field won't also be processed by Actions higher up in the GUI hierarchy.

However, KEY_PRESSED or KEY_RELEASED events will still not be consumed in the manner described above - which when related to the deviousness of KeyStroke's static construction methods can become a little confusing! (As you probably understand by this, it has at least confused me: Java insisted on firing some key bindings I had added for no-modified characters on a component high up in the hierarchy (some JPanel), for example the key 'i' and 't' and so on, even though a text field way lower in the hierarchy was focused and happily processed what I typed - so that when I wrote some text in the field, for example "He liked it!", I also fired off the global actions!)

It is worth noticing that the older Keymap functionality found on JTextComponents was reimplemented using the InputMap/ActionMap functionality when that was introduced in Java 1.3.

Semantic event firing: To round this off, we'll just do a quick round of the high-level events, or the semantic events as they're also called. Swing is built upon the Model-View-Controller ("MVC") architecture pattern. The details here are way beyond the scope of this article, but I'll do a quick scratch nevertheless (go here for more). First of all, the pattern was apparently invented by a Norwegian (as is object oriented programming too). Now, with the essentials out of the way: Lets start with a button. The View would the physical/visual button as you see it, while the Model is the button's state ("focused", "armed", "pressed" and clicked or on/off), and the Controller is the logic that transitions the model through these states (getting pinged by the view when the user interacted with it).

For Swing, the View and Controller is collapsed into a UI Delegate ("UI"), which forms a part of the Look and Feel ("LaF") system of Swing. This new model is apparently "sometimes referred to" as a Separable Model Architecture. The rationale for this you'll have to pry out yourself from the above-linked documentation - I've never quite understood it..!

You thus end up with three classes for a button: the JButton itself (which is an AbstractButton, which is a JComponent, which is a Component), which has a UI delegate of type ButtonUI, and has a model of type ButtonModel. There is no standard ButtonUI, as this is a part of the installed LaF, but there is a "place to start" called BasicButtonUI. The standard ButtonModel is DefaultButtonModel.

  • You instantiate a JButton. Since it is a JComponent and thus a Component, it can have all the low-level AWTEvents fired on it, if it wants.

  • The JButton constructor instantiates and sets a DefaultButtonModel, and then (effectively) invokes updateUI, which installs the current LnF-dictated ButtonUI.

  • While setting the model, the JButton also installs a bunch of listeners on it. This is so that changes on the model may be reflected physically on the screen by the JButton: If the model becomes pressed, the JButton needs to know so that it can repaint itself into a pressed-looking state.

  • Assuming that we're doing BasicUI (which is Okay, WindowsButtonUI extends BasicButtonUI), the UI installs a slew of listeners on the JButton (which, as mentioned, can have all low-level AWT events delivered to it). It does this by instantiating a BasicButtonListener (a helper for the BasicButtonUI) which implement all these listeners, and then installs this instance by invoking the different add*Listener(instance) methods on the JButton.

  • The BasicButtonListener obviously implements MouseListener.mousePressed and mouseReleased. On pressed, the BasicButtonListener, i.e. the UI delegate updates the model to be armed and pressed, and it furthermore requests focus to the button. ...

  • ... while on release, the BasicButtonListener, i.e. the UI delegate updates the model to be un-pressed.

  • Since the button was armed, this update to un-pressed state makes the model create and fire the ActionEvent!

  • (The BasicButtonListener (i.e. the UI delegate) then also un-arms the model, making it ready for a new round)

As a little side-note, if you press the mouse on the JButton, but then drag the mouse out of the JButton while still holding the mouse button, the hooking of an AWTEventListener in the Container to catch mouse dragged events, (as mentioned above on retargeting of MouseEvents) will make sure that the button is still focused and pressed, BUT the still-fired mouseExit event will make the DefaultMouseListener (i.e. the UI delegate) un-arm the model. So, if you now release the mouse button outside of the JButton, it will not fire.

Anyways, here's a stack trace for a clicked JButton having an ActionListener installed. I've included the AWT Events preceeding this ActionListener invocation:

52729 [AWT-EventQueue-0] AWTEvent: @856d3b java.awt.event.MouseEvent[MOUSE_PRESSED,(57,15),absolute(-305,317),button=1,modifiers=Button1,extModifiers=Button1,clickCount=1] on JButton
53995 [AWT-EventQueue-0] AWTEvent: @681db8 java.awt.event.MouseEvent[MOUSE_RELEASED,(57,15),absolute(-305,317),button=1,modifiers=Button1,clickCount=1] on JButton
53995 [AWT-EventQueue-0] java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=Button with AL,when=1236222620558,modifiers=Button1] on JButton
  com.example.ExampleActionListener.actionPerformed(...)
  javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
  javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
  javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
     ^^ Here we're processing the newly created semantic/high-level ActionEvent
  javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
     ^^ This invocation is setPressed(false), which, since the model was "pressed" and "armed", creates and fires the ActionEvent.
  javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
  java.awt.Component.processMouseEvent(Component.java:6216)
  javax.swing.JComponent.processMouseEvent(JComponent.java:3265)
  java.awt.Component.processEvent(Component.java:5981)
  java.awt.Container.processEvent(Container.java:2041)
  java.awt.Component.dispatchEventImpl(Component.java:4583)
  java.awt.Container.dispatchEventImpl(Container.java:2099)
  java.awt.Component.dispatchEvent(Component.java:4413)
     ^^ Here we're processing the newly created retargeted AWTEvent, on the JButton instance
  java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4556)
  java.awt.LightweightDispatcher.processMouseEvent(Container.java:4220)
  java.awt.LightweightDispatcher.dispatchEvent(Container.java:4150)
     ^^ Invocation of retargeting through LightweightDispatcher
  java.awt.Container.dispatchEventImpl(Container.java:2085)
  java.awt.Window.dispatchEventImpl(Window.java:2475)
  java.awt.Component.dispatchEvent(Component.java:4413)
     ^^ Here we're processing the low-level AWTEvent straight from the operating system, on the Window instance
  java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
     ["EDT pump"]

And with that, we're done!

Wednesday, February 11, 2009

Swing should be a standalone library

The public parts of AWT and Java2D should be Java’s sole interface to the native GUI system. Java2D is already accelerated, and one even have some amount of control of such features with VolatileImage and friends like BufferStrategy. I would personally don't mind more intimate control of and access to the hardware. And I wouldn't be terribly sad if there had to come a Java2D 2 and 3 to handle any new types of GUI hardware, in the same way we have java.io, NIO and More New IO.

Swing ought to be a standalone, separate, pure java library, built on top of AWT/Java2D. Actually, initially Swing was just that; a library that you had to download on the side, w/o any hidden magic. It was called JFC, Java Foundation Classes (to compare against MFC, Microsoft Foundation Class, a tradition that was upheld with JSP vs ASP and others) and is actually still available. With the release of the absurdly named "Java 2" (which gave rise to version names like "Java 2 SDK v1.3.0" and "J2SE v1.4.0"), the JFC was included with the java releases, and the downward spiral of spaghetti was started.

Even if one cannot at this point drop the backward compatibility for the current Swing (at least not for a couple of major java versions), one could simply abandon it - and make a refactored Swing2. Here one would drop all the extreme amount of cruft that has accumulated, and fix the problems and annoyances and incorporate the knowledge gained through years since Swing 1 in 1997. The native code of Swing that obviously crept in as long as Swing was a integral part of the JRE, would be ripped out and the necessary parts "ported back" to AWT/Java2D. Importantly, however, this new Swing 2 shoud never be included in the JRE: Applications would have to include it themselves. I know what you immediately think, but this is not as awful, sizewise, as one first might believe. By separating the subsystems themselves into distinct packages (check out Project Jigsaw), you'd get better code with fewer interdependencies. Swing needs all the separation it can possibly get. By using jarjar, only classes used by the application would be included. Possibly proguard can help shrink the jar size, and Pack 200 shrinks jars by about 60%. You probably already have 10-20 external jars for any little app. Given the enormous potential resulting by separating Swing into an external jar, I'd love to depend on this jar too!

Swing1 could at some point become an optional element that itself needs download. Given that the JRE by v6u10 goes exactly in this direction, that wouldn't be a problem at all. And I would already look forward to the complete rewrite in Swing3 - or me and some friends would fork Swing2 into Swing2NG or something if we didn’t like the direction Sun's Swing was taking - thereby getting progressive dynamics into the development!

On a related note: The project called Cacioavallo, proposed here by Roman Kennke, is a project which wants "to improve the AWT and Java2D interfaces to enable the implementation of external backends to AWT/Java2D". He (and apparently one more) finished it up, and it is now present in OpenJDK. This is awesome. Furthermore, he followed up by implementing AWT using Swing (all the widgets-parts). This has now been taken further to the point where to implement the entire "GUI Java", you apparently only need implement a single Graphics2D class onto a direct "pixel plane". All other is thus taken care of. He "proofed" this by making a full implementation using DirectFB (and had already made a version running on VxWorks).

Actually, pretty much everything this guy does seems brilliant: http://kennke.org/blog/.

This stuff reminds me of two extremely interesting projects which are similar to each other and yet the complete opposite of each other: SwingWT and SWTSwing. Too bad those seem dead.

Monday, September 8, 2008

Debug Listeners on AWT/Swing components

Being a newbie to the whole Swing stuff, I often wonder what events are fired on a given component for any given gesture or event.

I wonder no more!

Copy the following (ctrl-C), then activate some package node in a project in Eclipse (e.g. the obvious "com.example") and paste (ctrl-V).

import java.awt.Component;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.JButton;

public class DebugStatics {

public interface LineTaker {
void line(String msg);
}

public static final void main(String[] args) {
LineTaker out = new LineTaker() {
@Override
public void line(String msg) {
System.out.println(msg);
}
};
addDebugListeners(out, new JButton(), false);
}

public static void addDebugListeners(final LineTaker out, final Object component,
final boolean includeAllMoveEvents) {

// :: Find all add*Listener methods for supplied component.

List<Class<?>> listenerInterfaces = new ArrayList<Class<?>>();
List<Method> addListenerMethods = new ArrayList<Method>();
Method[] methods = component.getClass().getMethods();
for (Method method : methods) {
String name = method.getName();
if (name.startsWith("add") && name.endsWith("Listener")) {
Class<?>[] parameters = method.getParameterTypes();
if (parameters.length == 1) {
Class<?> interfaze = parameters[0];
if (interfaze.isInterface()) {
listenerInterfaces.add(interfaze);
addListenerMethods.add(method);
}
}
}
}

// :: Make handler for "super listener"

InvocationHandler handler = new InvocationHandler() {
long _lastEvent;
String _lastMethodName;
boolean _threadFired;
InvocationHandler _handler = this;

@Override
public synchronized Object invoke(@SuppressWarnings ("unused") Object proxy, Method method, Object[] args)
throws Throwable {
_lastEvent = System.currentTimeMillis();
String methodName = method.getName();
if (includeAllMoveEvents || !(methodName.endsWith("Moved") && methodName.equals(_lastMethodName))) {
out.line("event:[" + method.getName() + "] - on - [" + method.getDeclaringClass().getName()
+ "] - with - " + Arrays.asList(args) + ".");
if (!_threadFired) {
_threadFired = true;
new Thread("debug:Event Stream Breaker") {
@Override
public void run() {
while (true) {
long millisLeft;
synchronized (_handler) {
millisLeft = 400 - (System.currentTimeMillis() - _lastEvent);
if (millisLeft < 0) {
out.line("===== event stream break.");
_threadFired = false;
_lastMethodName = null;
break;
}
}
try {
Thread.sleep(millisLeft + 2);
continue;
}
catch (InterruptedException e) {
break;
}
}
}
}.start();
}
}
_lastMethodName = methodName;
return null;
}
};

// :: Make "super listener", "implementing" all the Listener interfaces

Object superListener = Proxy.newProxyInstance(DebugStatics.class.getClassLoader(), listenerInterfaces
.toArray(new Class<?>[0]), handler);

// :: Attach "super listener" using all add*Listener methods on supplied component

for (Method method : addListenerMethods) {
try {
method.invoke(component, superListener);
out.line(" ++ add*Listener: [" + method + "].");
}
catch (Throwable e) {
out.line("Got error when trying to invoke add*Listener method:[" + method + "]." + e);
}
}
}
}
Code discussion: The LineTaker comes from the need to have some flexibility regarding where the textual stream of events should end up (e.g. to System.out.println(...) or to some logger.debug(...)). The driver (main method) is just to check out the listener adding (and possibly also to find out which types of add*Listener methods a given AWT/Swing/Whatever class has). The reason for the Thread-forking is so that you more easily can understand which events "belongs" to each actual, physical gesture: Move the mouse into a button, wait a second, then press down mouse button, wait a second, release mouse button, wait a second, move out. Now the result event stream have embedded a break on every second-wait. The thread only lives long enough to write the break, then exits. The reason for the code regarding move-events suppression is that you get an awful lot of move-events without it - this code results in only one move event being recorded in a consecutive run of the same move event.

While dumping the code into this post, it hit me that the above method actually works for any class having add*Listener(AnyInterface listener) methods. That's why the type of the component argument is Object.

There's a pretty impressive amount of events being fired on a JButton for the simple act of using the mouse to click it!

Wednesday, March 5, 2008

Debugging and Profiling - same, not different

Why is it that Eclipse has such a great debugger, but such a extremely poor profiler?

I've used JProfiler and YourKit Profiler, and find both products interesting (however, JProfiler gets my money). However, they are a just that slight tad too awkward to fire up - I don't use them often enough, so it is not "in the fingers" in the same way I always run my programs through the Eclipse debugger.

The Eclipse profiler, when I've gotten it to work, which is a real PITA, is amazingly slow - I've never been able to profile even the simplest applications. I wonder if it doesn't use some XML format to transfer the profiling information from the profiled application to the profiler..

The Eclipse debugger, on the other hand, is a truly wonderful tool - for what it is made for.

Then, one suddenly starts to wonder: aren't these two things pretty much the same stuff? Both the debugger and the profiler needs to be able to inspect the objects on heap, control threads, inspect stacks. This has been recognized by the JVM vendors, where the two JVM interfaces, JVMPI (JVM Profiling Interface) and JVMDI (JVM Debugger Interface), have been unified into the JVMTI (JVM Tools Interface). So why aren't simply these two JVM tools indeed a unified power tool?

I've so many times wanted to just stop the application and check out what all the memory is used for right now - "Heap Walking" in JProfiler terminology. Or just do a quick analyze of where the CPU is being used from here on to there on, using for example two breakpoints. There are plenty other scenarios where the features of a debugger and a profiler overlap, and where just one of them doesn't quite cut it. Starting the JVM with both JProfiler and Eclipse attached actually seems to work in some fashion (as long as you don't pause JProfiler's handling threads, as the JProfiler GUI then freezes), but I always seem to get Threads in the debugger that when paused, don't give me the stack, but instead show "", causing Eclipse to pretty much freeze too, and it is hardly the perfect situation at any rate.

Here's some possibilities:

  • Take a heap snapshot at some certain breakpoint.

  • Pausing the application, then taking a "heap walking tour". Resume, then exercise the application by doing that thing that always messes up. Pause again, snapshot, compare difference (or use breakpoints instead of manual pauses).

  • Stepping the application, having one thread in pause. Make a heap snapshot of all objects that the current thread's stack points to, displaying it graphically along with the stack trace, arrows pointing out from each stack element.

  • .. and being able to expand a Thread's stack elements and view the actual variables that hold on to each part of the heap graph, visually using arrows pointing to the objects

  • When in "heap walking", have a more direct understanding of what threads hold on to what objects.

  • Start allocation recording and/or cpu recording (start profiling) when any thread reaches some breakpoint.

  • Do timing analysis (profiling) of a single thread between two breakpoints. Do this repeatedly while the application is being exercised, and aggregate.

  • By default pause the application at a OutOfMemoryError and do a heap snapshot.

  • (Admittedly only a profiling wish:) Like YourKit (unlike JProfiler), let heap snapshots be "full" (include the data) and standalone, with thread stack information, in that the entire heap is dumped to disk, so that it is possible to easily compare.


Basically - what I want is the integration of the features that a debugger has, and what a profiler has. In particular the combination of stack-inspection from the debugger with heap-inspection from the profiler, and the code-integrated features with breakpoints and exception-breakpoints that a debugger has (the now standard fully integrated IDE debugger), triggering the profiling and heap snapshotting features that a profiler typically has.

I think that if these JVM inspection tools wasn't made with one or the other application in mind but instead thought of as one unified unit, a much more powerful tool would emerge. (I nearly get that tech-drool when thinking of how cool it would be: "so, I believe this object should go out of scope at this breakpoint.. So, why isn't it GC-able now? Lets trace-to its GC roots..").

Now, I have understood that Netbeans have a much more integrated profiler than Eclipse. I really should check Netbeans out someday very soon..!

Monday, February 25, 2008

Too much verbosity in generics!

Why do I have to write the following bold part:
Map<SomeLongClass, SomeOtherThing<Another>> myPoint = new HashMap<SomeLongClass, SomeOtherThing<Another>>();

(Update 2009-03-03: As you probably already knew, I am not the only one realizing this!)

As far as I know, anything else than a verbatim copy of the stash that already is present in the declaration of that variable/field is a compile error. So why do I have to type it?? Argh..!

And OH so annoying Eclipse behaves if you haven't yet imported Map while writing that line - because if you've come to the point where you're about to write "Hash.." and then think that it would be reeeally nice if Eclipse could just autocomplete the rest of that shit for you, it doesn't quite work! And at that point it doesn't help to arrange imports either, because the code is currently to uncompilable for poor Eclipse to even understand what you're aiming at, so you'll have to delete up to the equals-sign, stash in a semicolon, then arrange imports, and finally start up with the "Hash..." again, now having autocomplete functionality at the ready. Grr..

Please, someone: I'd like Java itself to remove the need for that duplicated stupidity, and then Eclipse to be better at trying to compile partially if it doesn't understand the line the cursor is on (so that at least arrange imports would work at the described annoyance), and finally, I'd like some extra-super context-assist for writing the line in the first place - letting Eclipse know of the standard implementations of all Collections, and also picking up the classpath. For that matter, it could do such stuff all the time.

(And while I'm whining: Blogger doesn't handle <'s and >'s in "rich text" mode..)

Thursday, February 21, 2008

Java Garbage Collection details - "Garbage First" G1

For folks into the details of java, there's an interesting blog entry called "Our Collectors" over at Jon Masamitsu's blog, who apparently is a Sun GC guru. It has a nice low-down over the different collectors - what they are, where they are used, and how they can be combined.

Also, this is a good place for some insight into the G1 GC - "Garbage First", a new algorithm that is going to be the default in Java 7.

Some interesting discussion ensues, where Tony Printezis, apparently another Sun insider, chips in with comments.

http://blogs.sun.com/jonthecollector/entry/our_collectors

Also, don't miss this good whitepaper, Memory Management in the Java HotSpot™ Virtual Machine (pdf, April 2006).