The Life Cycle of a Java Event in JDK 1.1

Last updated 1998 July 17 by Roedy Green

Introduction

In JDK 1.1, events are handled quite differently from JDK 1.0.2. This essay will follow an single event from creation to cremation to give you an idea of how it all fits together. For details, and practical coding concerns, I suggest you read some other essays on events such as Richard Baldwin's instructional essays or Jan Newmarch's essay. I have written a similar essay for the older JDK 1.0.2 events.

Overview

Events are: Instead of events percolating up to parents as they did in JDK 1.0.2, any object or component can register itself as a listener, interested in hearing about a type of events originating in some other component. When the event arises, the source component processes the event by dispatching it to each of the registered listeners. Dispatching is synchronous, i.e. the listener handler routines are called directly while the calling dispatcher waits for the handler to complete. According to the specification, the listeners may be informed in any order, but the usual implementation is a queue, with listeners informed in the same order they were added. If you want to be absolutely sure of a fixed order of handling listeners, make your own chain and pass the event between them. Listeners pretty much only hear events they are interested in. They are not responsible for passing the event on to anyone else.

The new event model gives you three places to hook in your code to process events.

  1. via listeners. This is by far the most common way and the easiest to set up. The listener can be any object, e.g. the enclosing frame, the component itself, a non-gui code object, a "mom" object to mother a set of components. A given listener can service several sources, and a given source can notify several target listeners. Listener methods have names like actionPerformed, windowClosed, mouseMoved. Listeners are often implemented with Adapter classes so you don't have to write dummy methods for events you are not interested in. Adapter classes have names like: java.awt.event.FocusAdapter, KeyAdapter, and WindowAdapter. Note, there is no such thing as ActionAdapter since it has only one method. Often you use inner classes or anonymous classes to handle fielding events by extending an adapter class. Be very careful in extending adapter classes to get your method names and signatures exactly right. Otherwise you won't override the corresponding dummy method, and your code won't ever be executed, leaving you wondering where all the events went.
  2. via processEvent which receives all raw events for the component. It does not see events coming in via listeners. If you override it, be sure to call super.processEvent to ensure all the finer event handlers, like processKeyEvent, ProcessActionEvent etc. also get called and the listeners alerted. If you attempt to restrict the events coming to processEvent with enableEvents(mask), if there are listeners registered for events not in the mask, those event classes will arrive anyway.
  3. via processXXXX(SomeEventClass e), e.g. processActionEvent, processItemEvent. Every raw event for the component of that class comes through here. It does not see events coming in via listeners. Be sure to call super.processXXXX to ensure the listeners are alerted.
A component might quite reasonably register itself as a listener for events arising in itself or for events arising from the associated native GUI. There is another deprecated technique to process events arising in itself. A component could override its processEvent routine, do some action, then call super.processEvent (which would inform all the listeners), then do some other action.

The Cast Of Players

Methods for registering a target as a listener of events arising in some source include: source.addActionListener(target), addAdjustmentListener, addComponentListener, addContainerListener, addFocusListener, addItemListener, addKeyListener, addMouseListener, addMouseMotionListener, addPropertyChangeListener, addTextListener, addVetoableChangeListener, addWindowListener ... Normally you would invoke these methods but not override them.

The AWT informs the list of registered listeners that an event has occurred by calling the generic source.processEvent which in turn calls the more specific event handler: source.processActionEvent, processAdjustmentEvent, processComponentEvent, processContainerEvent, processItemEvent, processKeyEvent, processMouseEvent, processMouseMotionEvent, firePropertyChange, processTextEvent, fireVetoableChange, processWindowEvent ... These routines would in turn use the AWTEventMulticaster to inform all the listeners and invoke their actionPerformed, keyPressed, windowOpened etc. methods in turn. These processXXX routines typically work by calling the relevant dispatching routines on the listener targets. It is unlikely you will ever deal with these methods directly.

Your Listener object must implement one or more interfaces such as: ActionListener, ComponentListener, ContainerListener, FocusListener, KeyListener, ItemListener, MouseListener, MouseMotionListener, TextListener, WindowListener ...

The easiest way to implement those interfaces is to extend one of the related adapter classes. The adapter classes provide dummy methods to deal with the subcategories of events that are of no interest to you. ComponentAdapter, ContainerAdapter, FocusAdapter, KeyListener, ItemAdapter, MouseAdapter, MouseMotionAdapter, TextAdapter, WindowAdapter ...

The event is dispatched to each target listener by invoking its listening method. These have names like: target.actionPerformed, adjustmentValueChanged, componentHidden, componentMoved, componentResized, componentShown, componentAdded, componentRemoved, focusGained, focusLost, itemStateChanged, keyPressed, keyReleased, keyTyped, mouseClicked, mouseEntered, mouseExited, mousePressed, mouseReleased, textValueChanged, windowActivated, windowClosed, windowClosing, windowDeactivated, windowDecionified, WindowIconified, windowOpened ... You need to write custom versions of these routines.

If there is a listener for an event type registered, then events from the GUI for that event type will be delivered to that component's processEvent. The component can control which additional types are delivered to processessEvent by calling enableEvents with a mask. This way events that no one is interested in are not even delivered to processEvent. It is unlikely you will be directly involved with enableEvents.

A key point to understand is that a component has two kinds of acceptor routines for events.

  1. The generic processEvent routine primarily accepts events indirectly coming from the native GUI. Application programmers do not normally use these methods.
  2. The listener methods, e.g. actionPerformed, accept events from other components. This is the main tool for application programmers to deal with events. Component writers can also use the listener interface by registering a custom component as a listener to events arising in itself.

The Life Cycle Of An Event

Where do Events Come From Daddy?

You synthesize an AWTEvent and post to the SystemEventQueue for later delivery. In this Alice in Wonderland world, you set the source field of the event to the component or peer where you want the event delivered. Later, when the source delivers the event to the various listeners, the term source for this field will make more sense. A separate thread will service the queue asynchronously and deliver the event to the target's ProcessEvent method. When the event percolates to the head of the queue, the dispatcher will call event.getSource().processEvent(event);

Most events arise in the native GUI. They can enter the Java system in one of two ways:

  1. Notification of interesting happenings in the GUI arrive as a single stream of messages mentioning native GUI components. This is the traditional native GUI event loop handled by hidden code, that calls getMessage() in a loop. The AWT uses a hash table to convert references to native GUI components into the corresponding Java components and peers, and composes corresponding Java events and enqueues them in the SystemEventQueue.
  2. Java peer components generate events based on information they glean from the native GUI. These too are enqueued in the SystemEventQueue.
Lightweight components generate events and post them directly to themselves, without going through the SystemEventQueue.

Where is the Event Loop?

People who cut their teeth in the Windows or Mac native low level GUI API are used to writing application-specific code to handle dispatching using an event processing loop. The AWT handles this all automatically and invisibly in EventDispatchThread.run. It reads the the stream of native GUI event messages, and translates them, creating Java events, which it puts into the SystemEventQueue. The thread that processes events, when it finishing processing one event, goes and picks up another event off the head of the event queue to dispatch. This is very thread that executes most of your application code.

The loop that processes events does not even start until your main program has finished executing!! This event loop code is not visible to the application programmer.

Creation

Consider what happens behind the scenes when the user clicks an OK button on the screen. The underlying native GUI notices the mouse click.
It could then work one of two ways:
  1. The native GUI indirectly notifies the Java Button peer object which creates a new event using the ActionEvent constructor.
  2. The GUI creates a native format message that a button has been clicked. Hidden code in the AWT reads this stream of messages and translates this one into a new event using the ActionEvent: constructor. It has a hashtable to convert references to native peer components to the corresponding Java components.
  3. ActionEvent e = new java.awt.event.ActionEvent(myButton,
                                    ActionEvent.ACTION_PERFORMED,
                                    "OK",
                                    0);
    
    ActionEvent(Object source,
                int id,
                String command,
                int modifiers);
    source
    A Java component closely associated with this event, namely myButton. At first this field acts as the destination for the event. Later it informs listeners where the event came from.
    id
    the type of event, in this case ActionEvent.ACTION_PERFORMED.
    command
    the command string for this action event. In this case it would be the string "OK".
    modifiers
    the modifiers held down during this click, e.g. CTRL_MASK if the Ctrl key is held down

Enqueuing

The newly created event is then enqueued on the system event queue using:
   EventQueue q = Toolkit.getDefaultTookit().getSystemEventQueue();
Now it can enqueue the event for delivery to the source (myButton) with the system's postEvent method. We don't use any methods of myButton just yet.
   q.postEvent(e);
The event hibernates in the queue waiting its turn to be dispatched. postEvent will return immediately. It won't wait until the new event has been delivered.

Dispatching

There is a single system thread that grabs the next item off the queue and dispatches it. You can see the code for it in EventDispatchThread.run When all the processing for that event is complete, it gets the next event. No other event processing, not even a mouse move, can happen until the previous event has been processed. I repeat, only one event is processed at at time.

Having said that, this single-thread behaviour is not part of the specification. There are some JVMs where you have several threads processing events simultaneously.

It looks at the source field in the event object, and delivers the event to that source object.

First it checks the eventMask to be sure the myButton component is willing to accept that flavour of event. The button could conceivably have used disableEvents to filter out events such as ours. It also checks if there are listeners for our type of event. If there are, even if our event type is blocked, our event will still be delivered.

It delivers the event to the button component by calling myButton's generic processEvent event handler:

   myButton.processEvent(e);
or more precisely:
   e.getSource().processEvent(e)
This dispatching mechanism is efficient because it does not require any sort of table lookup or nested switch statements.

Classification

processEvent could in theory do some special processing on the event, but usually all it does is classify the event and call a more specialised event handler such as: processActionEvent. (If you override processEvent, make sure you call super.processEvent to ensure the standard event processing also occurs.) Had we been processing a mouse event, we would have called processMouseEvent instead.

Notifying Listeners

A long time ago, before this event was born, parties interested in this type of event registered their interest my using myButton.addActionListener(myListener). It is important to remember that addXXXXListener methods are implemented by the sources, not the listeners. In theory, the addXXXXListener methods could be invoked by the source or the listener, but usually they are invoked by a third party, usually an enclosing frame.

Raw events arrive via processEvent. They are classsified and redirected to more specialised event processors like processActionEvent.

processActionEvent can further classify the incoming events and can do whatever special processing it wants. Then it notifies any listeners, one after the other. (If you override processActionEvent, make sure you call super.processActionEvent to ensure the listeners are notified.)

Each listener effectively gets a fresh cloned copy of the original event. Unfortunately, this wastes cpu cycles and creates a ton of run time garbage. The intent is to make events effectively read-only and to render harmless any malicious component that stored a reference to an event. Once an event enters Component.processKeyEvent, it isn't replicated anymore so presumably some of its fields could be modified and used for communication. Ideally event objects would be read-only and recyclable.

There is no guarantee that the listeners will be notified in any particular order, however they will be notified one at a time, never in parallel.

To notify each registered Listener, the speaker's processActionEvent calls each listener's actionPerformed(e) method by chasing the AWTEventMultiCaster Listener chain.

AWTEventMulticaster contains generic code that most components use for implementing addXXXListener and the notification of listeners. Component has various add/remove Listener methods that use AWTEventMulticaster that all Components inherit.

If we had been processing a mouse motion, we would have called the listener's mouseMoved method instead. processActionEvent methods are designed to be called by processEvent; methods like actionPerformed and mouseMoved are part of Listener interfaces. Most application coding uses only Listeners. Only if you were inventing a new sort of component would you get involved with processEvent and processActionEvent.

What Are Listeners?

A listener is any object that
  1. implements one of the listener interfaces such as ActionListener. E.g. an ActionListener must implement an actionPerformed method to be called when an Action event occurs.
  2. Has hooked itself (or been hooked by a third party) onto one or more event generators with a method such as addActionListener.
A listener might be a component, an ordinary object, a special Mom object to mother a set of components or an anonymous object created from an anonymous class to simply provide a hook to some other routine.

A listener might be created by extending one of the adapter shortcut classes. The adapter classes give you some dummy classes to start with in defining a listener where you only want to handle a few types of event.

A component may even register itself as a listener for events originating it itself.. This is the preferred method for application programmers since there is less possibility of mucking up standard event processing.

A listener can field events from many different sources. Similarly a source may notify many different listeners. Alternatively you could have a separate listener object for every source, or anything in between.

What does the Listener do?

What does the listener do when it's actionPerformed (or other mouseMoved etc...) method is called? It may classify the incoming event based on source, id or class (via instanceof). Then it would perform some action, do a calculation, change the look of the screen, delete a file... If it wants, it can use the consume() method to mark the event consumed to discourage further processing on it.

It had better be quick! The entire system is at a standstill, unable to process any more events until your listener completes its action. If the action is going to take more than a few milliseconds you should either:

In a similar vein, if it is very important that some action not start until after the physical paint is finished, the simplest way to arrange that is to have paint kick it off, perhaps by examining some flag to see if kickoff is necessary.

How Does consume() Work?

I have not found a precise definition of what is supposed to happen to a consumed event. By experiment, I discovered it seems to stop further processing by the base classes of the current component. It does not seem to stop notification of others listeners. I found for example that in TextField, consuming a keystroke event would prevent it being displayed. However, this does not work with the KL group JCTextField or the Swing JTextField. There you must override processKeyEvent.

After all the listeners have been called, the event is checked to see if it has been consumed. If so, then it is not passed onto the next stage. For e.g. KeyEvents for a TextArea, this means that it is not passed back to the peer, and so does not reach the underlying window/widget, and so does not appear.

consume() in KeyListeners is effectively broken in Swing 0.7 since it is ignored. It looks like the event is drawn in JTextArea before being given to the listeners.

For TextFields, you can change the value of the keystroke in the event in a KeyListener before passing it on, e.g. convert it to lower or upper case. However under Motif, converting to lower case is broken, and the entire technique is broken for Swing. You would have to trap processKeyEvent instead.

How Do You Compose a Listener?

Java 1.1 added inner classes and anonymous classes to make it easier to hook up Listener code. Here is code that could appear in the middle of a frame initialisation method that:
  1. defines a new anonymous class that extends theWindowAdapter listener.
  2. defines a method in that class to deal with window closing events.
  3. allocates an object of that class.
  4. hooks that object up as a listener to the current frame component.
  5. throws away the reference to the class. (The object won't die since it is on the list of listeners.)
class MyFrame extends Frame {
  // constructor
  MyFrame ()
  {
  setSize(200,300);
  this.addWindowListener( new java.awt.event.WindowAdapter()
    { // anonymous WindowAdapter class
       public void windowClosing (java.awt.event.WindowEvent w)
       {
       MyFrame.this.setVisible(false);
       MyFrame.this.dispose();
       } // end windowClosing
    } // end anonymous class
  ); // end addWindowListener invocation
  setVisible(true);
  } // end constructor
}  // end class MyFrame

Cremation

The various listeners eventually complete their work and return to actionPerformed. The return stack unwinds: actionPerformed returns. processActionEvent returns. processEvent returns. We are now back at system the code that dispatches events from the queue. Our event is forgotten and the next event takes the stage. With no more references to our old event, garbage collection soon recycles the RAM the event occupied.

Generalising this Example

Instead of created an ActionEvent with id ActionEvent.ACTION_PERFORMED, that was dispatched to processEvent, then passed on to processActionEvent, then passed on to an ActionListener (that registered its interest via addActionListener) via its actionPerformed method, there are many other possibilities you can generate from the following table:

Event Class id Specific
Event
Handler
Listener
Interface
Listener
Notification
Method
Listener
registration
Adapter shortcut
ActionEvent ACTION_PERFORMED processActionEvent ActionListener actionPerformed addActionListener
AdjustmentEvent ADJUSTMENT_VALUE_CHANGED processAdjustmentEvent AdjustmentListener
ScrollPane
adjustmentValueChanged addAdjustmentListener
ComponentEvent COMPONENT_HIDDEN
COMPONENT_MOVED
COMPONENT_RESIZED
COMPONENT_SHOWN
processComponentEvent ComponentListener componentHidden
componentMoved
componentResized
componentShown
addComponentListener ComponentAdapter
ContainerEvent COMPONENT_ADDED
COMPONENT_REMOVED
processContainerEvent ContainerListener componentAdded
componentRemoved
addContainerListener ContainerAdapter
FocusEvent FOCUS_GAINED
FOCUS_LOST
processFocusEvent FocusListener focusGained
focusLost
addFocusListener FocusAdapter
KeyEvent KEY_PRESSED (e.g.shift, F1)
KEY_RELEASED
KEY_TYPED (eg.a A)
processKeyEvent KeyListener keyPressed
keyReleased
keyTyped
addKeyListener KeyAdapter
ItemEvent DESELECTED
ITEM_STATE_CHANGED
SELECTED
processItemEvent ItemListener ItemStateChanged addItemListener
MouseEvent MOUSE_CLICKED
MOUSE_ENTERED
MOUSE_EXITED
MOUSE_PRESSED
MOUSE_RELEASED
processMouseEvent MouseListener mouseClicked
mouseEntered
mouseExited
mousePressed
mouseReleased
addMouseListener MouseAdapter
MouseEvent MOUSE_DRAGGED
MOUSE_MOVED
processMouseMotionEvent MouseMotionListener mouseDragged
mouseMoved
addMouseMotionListener MouseMotionAdapter
PaintEvent PAINT
UPDATE
paint
update
TextEvent TEXT_VALUE_CHANGED processTextEvent TextListener textValueChanged addTextListener
WindowEvent WINDOW_ACTIVATED
WINDOW_CLOSED
WINDOW_CLOSING
WINDOW_DEACTIVATED
WINDOW_DEICONIFIED
WINDOW_ICONIFIED
WINDOW_OPENED
processWindowEvent WindowListener windowActivated
windowClosed
windowClosing
windowDeactivated
windowDecionified
WindowIconified
windowOpened
addWindowListener WindowAdapter

For a complete list of the various listener interfaces, and methods see C:\jdk1.1.5\docs\api\Package-java.awt.event.html. You want to study the whole java.awt.event package.

Censoring Keystrokes

Let us say you wanted to write your own version of TextField-like component that disallowed certain keystrokes, or that processed certain keystrokes specially. You may have tried the easy Listener/consume() method and discovered it did not work for your base component. Here is bigger hammer to use:
protected void processKeyEvent (KeyEvent e)
    {
    switch (e.getID())
        {
        case KeyEvent.KEY_PRESSED:
             switch (e.getKeyChar())
                {
                case KeyEvent.CHAR_UNDEFINED:
                     nonUnicodeKeyPressed(e);
                     break;
                case KeyEvent.VK_ENTER:
                case KeyEvent.VK_TAB:
                case KeyEvent.VK_BACK_SPACE:
                case KeyEvent.VK_DELETE:
                     unicodeControlKeyPressed(e);
                     break
               default:
                     unicodeKeyPressed(e);
                     break;
                } // end inner switch getKeyChar
             break;
        case KeyEvent.KEY_RELEASED:
        case KeyEvent.KEY_TYPED:
        default:
             break;
        } // end outer switch getID()
    if (!e.isConsumed()) super.processKeyEvent(e);
    } // end processKeyEvent
If you want the underlying base class not to see the event, you consume it, and that suppresses the call to super.processKeyEvent. Something to baffle you. Hitting the Enter key under NT generates two keystrokes a \r and a \n. On Other platforms you get just a \n. Best just to look for VK_ENTER.

Activating a button

Hooking up some code to be executed when a button is hit is very similar to the earlier WindowClosing example. There is no such thing as the ActionAdapter class, so we use the name of the interface instead.
myButton.addActionListener( new java.awt.event.ActionListener()
    { // anonymous class that implements the ActionListener interface
       public void actionPerformed (java.awt.event.ActionEvent e)
       {
       System.out.println("Somebody hit the button.");
       } // end actionPerformed
    } // end anonymous class
  ); // end addActionListener invocation

Missing Events

The newsgroups are full of plaintive messages from newbies claiming the AWT is broken because their Listeners are not receiving any events. Here are some things to check:

Event vs. AWTEvent

In JDK 1.0.2 there was basically only one kind of event, called the java.awt.Event. In JDK 1.1 there is a base event called a java.awt.event.AWTEvent. All the other types of event such as java.awt.event.actionEvent and java.awt.event.windowEvent are derived from it. It is a bit confusing since the event package for JDK 1.1 is called java.awt.event instead of java.awt.AWTEvent as you might expect.

Changes from JDK 1.0

In addition to the complete revamping of how it works, some of the details have changed too. The names of the various events no longer live in the Event class. They live in the various subclasses of AWTEvent e.g. WindowEvent.WINDOW_CLOSED. Some events have been renamed. WINDOW_DESTROY is now WINDOW_CLOSED. You can no longer access the source and id fields of an event directly. You must call getSource and getID. handleEvent is now called ProcessEvent.

Summary

The key point to understand is that a component has two kinds of acceptor routines for events. The generic processEvent routine primarily accepts events coming indirectly from the native GUI which it then fobs off on the more specific processXXXXEvent methods. The listener methods, e.g. actionPerformed, accept events from other components. The listener methods are primarily for application programmers and those who customise existing components; the processXXXXEvent methods are primarily for the writers of radically new components.

Repaint

You may have seen mention of PaintEvent.PAINT and PaintEvent.UPDATE events. These are used to control how components are repainted. The repaint mechanism partly uses the SystemEventQueue and partly the queue inside the native GUI. See Repaint in the Java Glossary for details on how it works.

Credits

This article would not have been possible without the patient assistance of Jan Newmarch who researched the material. Jan has a a chapter in a SAMS Book Tricks Of The Java Gurus coming out soon. Richard Baldwin and Peter Mehlitz helped explain the JDK 1.1 event processing. Steve Odendahl pointed out the mismatched signature problem in Adapters. Tuyen Tran pointed out the problems with Swing and threading.

HTML Checked!
Canadian Mind Products You can get an updated copy of this page from http://mindprod.com/event11.html