Java AWT: Delegation Event Model


Last updated: October 25, 1996

Purpose

This document explains the rationale behind introducing a new event model into the AWT and describes specifically how the new model maps to the AWT API. This new model has also been adopted by the JavaBeans architecture for general event processing and is described at a high level in the JavaBeans Specification document.

The 1.0 Event Model

The model for event processing in version 1.0 of the AWT is based on inheritance. In order for a program to catch and process GUI events, it must subclass GUI components and override either action() or handleEvent() methods. Returning "true" from one of these methods consumes the event so it is not processed further; otherwise the event is propagated sequentially up the GUI hierarchy until either it is consumed or the root of the hierarchy is reached. The result of this model is that programs have essentially two choices for structuring their event-handling code:
  1. Each individual component can be subclassed to specifically handle its target events. The result of this is a plethora of classes.
  2. All events for an entire hierarchy (or subset thereof) can be handled by a particular container; the result is that the container's overridden action() or handleEvent() method must contain a complex conditional statement in order to process the events.

Issues with the 1.0 Event Model

While the above model works fine for small applets with simple interfaces, it does not scale well for larger java programs for the following reasons:


The Delegation Model

The JDK1.1 will introduce a new delegation-based event model in AWT in order to:

Design Goals

The primary design goals of the new model in the AWT are the following:

Note: These goals are described from the particular perspective of the AWT. Since this model has also been designed to accommodate the JavaBeans architecture, the design goals from the JavaBeans perspective are described in the "Events" section of the JavaBeans Specification and may vary slightly from these goals.

Delegation Model Overview

Event types are encapsulated in a class hierarchy rooted at java.util.EventObject. An event is propagated from a "Source" object to a "Listener" object by invoking a method on the listener and passing in the instance of the event subclass which defines the event type generated.

A Listener is an object that implements a specific EventListener interface extended from the generic java.util.EventListener. An EventListener interface defines one or more methods which are to be invoked by the event source in response to each specific event type handled by the interface.

An Event Source is an object which originates or "fires" events. The source defines the set of events it emits by providing a set of set<EventType>Listener (for single-cast) and/or add<EventType>Listener (for mult-cast) methods which are used to register specific listeners for those events.

In an AWT program, the event source is typically a GUI component and the listener is commonly an "adapter" object which implements the appropriate listener (or set of listeners) in order for an application to control the flow/handling of events. The listener object could also be another AWT component which implements one or more listener interfaces for the purpose of hooking GUI objects up to each other.

Event Hierarchy

Events are no longer represented by a single Event class (like java.awt.Event) with numeric ids, but instead by a hierarchy of event classes. Each event class is defined by the data representing that event type or related group of events types.

Since a single event class may be used to represent more than one event type (i.e. MouseEvent represents mouse up, mouse down, mouse drag, mouse move, etc), some event classes may also contain an "id" (unique within that class) which maps to its specific event types.

The event classes contain no public fields; the data in the event is completely encapsulated by proper get<Attr>()/set<Attr>() methods (where set<Attr>() only exists for attributes on an event that could be modified by a listener).

Although these are the concrete set defined by the AWT, programs are free to define their own event types by subclassing either java.util.EventObject or one of the AWT event classes.

Low-level vs. Semantic Events

The AWT provides two conceptual types of events: low-level and semantic.

A low-level event is one which represents a low-level input or window-system occurrence on a visual component on the screen. The low-level event classes defined by the AWT are as follows:

	java.util.EventObject
		java.awt.AWTEvent 
		    java.awt.event.ComponentEvent (component resized, moved, etc.)
			java.awt.event.FocusEvent (component got focus, lost focus)
			java.awt.event.InputEvent
			    java.awt.event.KeyEvent (component got key-press, key-release, etc.)
			    java.awt.event.MouseEvent (component got mouse-down, mouse-move, etc.)

Semantic events are defined at a higher-level to encapsulate the semantics of a user interface component's model. The semantic event classes defined by the AWT are as follows:

	java.util.EventObject
	    java.awt.event.AWTEvent
		java.awt.event.ActionEvent ("do a command")
		java.awt.event.AdjustmentEvent ("value was adjusted")
		java.awt.event.ItemEvent ("item state has changed")

Note that these semantic events are not tied to specific screen-based component classes, but may apply across a set of components which implement a similar semantic model. For example, a Button object will fire an "action" event when it is pressed, a List object will fire an "action" event when an item is double-clicked, a MenuItem will fire an "action" event when it was selected from a menu, and a non-visual Timer object might fire an "action" when its timer goes off (the latter is a hypothetical case).

Event Listeners

An EventListener interface will typically have a separate method for each distinct event type the event class represents. So in essence, particular event semantics are defined by the combination of an Event class paired with a particular method in an EventListener. For example, the FocusEventListener interface defines two methods, focusGained() and focusLost(), one for each event type that FocusEvent class represents.

The API attempts to define a balance between providing a reasonable granularity of Listener interface types and not providing a separate interface for every single event type.

The low-level listener interfaces defined by the AWT are as follows:

	java.util.EventListener
	    java.awt.event.ComponentListener 
	    java.awt.event.FocusListener 
	    java.awt.event.KeyListener 
	    java.awt.event.MouseListener 
	    java.awt.event.MouseMotionListener 
	    java.awt.event.WindowListener

The semantic listener interfaces defined by the AWT are as follows:

	java.util.EventListener
	    java.awt.event.ActionListener 
	    java.awt.event.AdjustmentListener 
	    java.awt.event.ItemListener 

Event Sources

Because the events fired by an event source are defined by particular methods on that object, it is completely clear from the API documentation (as well as by using run-time introspection techniques) exactly which events an object supports.

All AWT event sources support a multicast model for listeners. This means that multiple listeners can be added and removed from a single source. The API makes no guarantees about the order in which the events are delivered to a set of registered listeners for a given event on a given source. Additionally, any event which allows its properties to be modified (via setXXX() methods) will be explicitly copied such that each listener receives a replica of the original event. If the order in which events are delivered to listeners is a factor for your program, you should chain the listeners off a single listener which is registered on the source (the fact that the event data is encapsulated in a single object makes propagating the event extremely simple).

Event delivery is synchronous (as with 1.0's handleEvent()), however programs should not make the assumption that the delivery of an event to a set of listeners will occur on the same thread.

Once again, a distinction is drawn between low-level and semantic events. For low-level events, the source will be one of the visual component classes (Button, Scrollbar, etc) since the event is tightly bound to the actual component on the screen. The low-level listeners are defined on the following components:

	addComponentListener(ComponentListener l)
	addFocusListener(FocusListener l)
	addKeyListener(KeyListener l)
	addMouseListener(MouseListener l)
	addMouseMotionListener(MouseMotionListener l)
	addWindowListener(WindowListener l)
	addWindowListener(WindowListener l)

For semantic events, the source is typically a higher-level interface representing the semantic model (and this higher-level interface is commonly `implemented' by components using the model). Following are the semantic listeners defined for AWT components:

	addActionListener(ActionListener l)
	addItemListener(ItemListener l)
	addItemListener(ItemListener l)
	addItemListener(ItemListener l)
	addActionListener(ActionListener l)
	addItemListener(ItemListener l)
	addActionListener(ActionListener l)
	addAdjustmentListener(AdjustmentListener l)
	addActionListener(ActionListener l)

Adapters

Since many of the EventListener interfaces are designed to listen to multiple event subtypes (i.e. the MouseListener listens to mouse-down, mouse-up, mouse-enter, etc.), the AWT will provide a a set of default "adapter" classes, one which implements each listener interface. This will allow programs to easily subclass the Adapters and override ONLY the methods representing event types they are interested in.

The Adapter classes provided by AWT are as follows:

	java.awt.event.ComponentAdapter
	java.awt.event.FocusAdapter
	java.awt.event.KeyAdapter
	java.awt.event.MouseAdapter
	java.awt.event.MouseMotionAdapter
	java.awt.event.WindowAdapter

Note: There are no default Adapters provided for the semantic listeners, since each of those only contain a single method and an adapter would provide no real value.

Filtering for Performance

One of the great benefits of the new model is that since listeners are registered to handle specific event types, it's relatively easy for the toolkit to filter the events and deliver ONLY the events a component is interested in. This was not true with the old model!. Filtering should improve performance, especially with high frequency type events, such as mouse-moves.

All platforms should see some performance improvement from reduced event traffic, but the Solaris implementation should gain exceptional improvement since it's a network-based window system.

Code Example

Following is some sample code that uses the new model:
	import java.awt.*;
	import java.awt.event.*;
	public class App {
	    public void search() {// do search operation...}
	    public void sort() {// do sort operation ... }
	    static public void main(String args[]) {
		App app = new App();
		GUI gui = new GUI(app);
	    }
	}
	class Command implements ActionListener  {
	    static final int SEARCH = 0;
	    static final int SORT = 1;
	    int id;
	    App app;
	    public Command(int id, App app) {
		this.id = id;
		this.app = app;
           }
	    public void actionPerformed(ActionEvent e) {
		switch(id) {
		   case SEARCH: 
			app.search();
			break;
		   case SORT:
			app.sort();
			break;
		}
	    }
	}
	class GUI {
	    public GUI(App app) {
		Frame f = new Frame();
		f.setLayout(new FlowLayout());		
		Command searchCmd = new Command(Command.SEARCH, app);
		Command sortCmd = new Command(Command.SORT, app);
		Button b;
		f.add(b = new Button("Search"));
		b.addActionListener(searchCmd);
		f.add(b = new Button("Sort"));
		b.addActionListener(sortCmd);
		List l;
		f.add(l = new List());
		l.add("Alphabetical");
		l.add("Chronological");
		l.addActionListener(sortCmd);
		f.pack();
		f.show();
	    }
	}

Note in particular the following differences between this example and how this would have been implemented in the old model:


Handling Events in Extended Components

For Java programs which are extending component classes via subclassing, it would be burdensome to require the registration of separate listener objects to respond to events. Therefore, for this case, the AWT defines that each component provide specific protected methods (that can be overridden by subclasses) which actually dispatch the events to listeners if they exist. This way a subclass can simply override one of these methods in order to process an event.

In order to make this as flexible as possible, this event processing capability is provided in two levels. The first is a single method on all components:

     protected void processEvent(AWTEvent)
All events for a component are first funneled through this method so that subclasses can choose to handle all events in a single place (similar to the 1.0 model's "handleEvent" with the main difference being that events are NOT propagated up the containment hierarchy in the new model).

The second option for processing events is provided at the event class level; there is a separate method for each class of event handled by that component:

     protected void processEventClass(EventClass)
For example, the java.awt.List component has the following event-class processing methods:
     protected void processActionEvent(ActionEvent e)

     protected void processItemEvent(ItemEvent e)
By default, the single processEvent method will invoke the proper event-class processing method. The event-class processing method by default will invoke any listeners which are registered. It's important to remember that these methods perform a critical function in the event processing for an AWT component and so if you override them you should remember to call the superclass's method somewhere within your own!

Selecting for Event Types

One of the goals of the listener model is to improve performance by NOT delivering events which components are not interested in. By default, if a listener type is not registered on a component, then those events will NOT be delivered and these processing methods will therefore NOT be called. So if you are using this extension mechanism for event-handling, you'll need to select for the specific types of events your component wishes to receive (in case no listener is registered). This can be done by using the following method on java.awt.Component:
     protected final void enableEvents(long eventsToEnable)
The parameter to this method is a bitwise mask of the event types you wish to enable. The event masks are defined in java.awt.AWTEvent. Note that changing this mask will not affect the delivery of events to listeners -- it only controls the delivery to the component's processing methods. The bottom line is that the set of events which are delivered to processEvent() is defined by the union of event types which have listeners registered and event types explicitly turned on via enableEvents().

Example using Extension Mechanism

Following is an example of how this extension mechanism may be used. For Example, if a subclass of java.awt.Canvas wishes to render some visual feedback when it receives/loses keyboard focus, it could do the following.
     
    public class TextCanvas extends Canvas {
	  boolean haveFocus = false;

	  public TextCanvas() {
	      enableEvents(AWTEvent.FOCUS_EVENT_MASK); // ensure we get focus events
	      ...
	  }
	  protected void processFocusEvent(FocusEvent e) {
	      switch(e.getId()) {
	        case FocusEvent.FOCUS_GAINED:
	          haveFocus = true;
	          break;
	        case FocusEvent.FOCUS_LOST:
	          haveFocus = false;
	      }
	      repaint(); // need to repaint with focus feedback on or off...

	      super.processFocusEvent(e); // let superclass dispatch to listeners
	  }
	  public void paint(Graphics g) {
	      if (haveFocus) {
	          // render focus feedback...
	      }
	  }
	  ...rest of TextCanvas class...
    }

A Word of Caution

In general we recommend you use the delegation-based listener model for most of your basic event handling needs and reserve the use of the above when you are truly extending the look or behavior of a component. This is because the above mechanism suffers from some of the same ills as the 1.0 event model (complex, error-prone logic in processing methods, forgetting to call "super.processEvent", etc.), and unless you clearly understand what you are doing, your program may not behave as you expect!

Consuming Events

There are cases where programs need to prevent certain types of events from being delivered to the native peer (i.e. a builder wants to use mouse events to enable a user to graphically move a button around and it wants to prevent the mouse press from 'pushing' the button).

It's important to understand that listeners do NOT have the capability of consuming the events they receive (that is why they are called "listeners" and not "handlers"). However, we do allow this consumption when using the extension mechanism described above -- for input events ONLY. This capability is provided through the boolean return value of the input event processing methods on java.awt.Component:

     protected boolean processKeyEvent(KeyEvent e)

     protected boolean processMouseEvent(MouseEvent e)

     protected boolean processMouseMotionEvent(MouseEvent e)
By default these methods always return "false", which will ensure the event is delivered normally and the component will function in the default manner. If your subclass overrides one of these methods and returns "true" (to consume the event), the component will NOT respond to the event in the default manner.

The Event Queue

Another feature of the 1.1 event model is the addition of a system event queue object, which is represented by the class:
     java.awt.EventQueue
All events generated on components are now first posted to this queue before they are dispatched to their target component (a separate thread is created by the queue which does the dispatching in the run method).

The EventQueue class provides a static method to access the handle of the system EventQueue instance:

     public static EventQueue getEventQueue()
as well as a number of public instance methods to manipulate the queue:
     public synchronized void postEvent(AWTEvent e)

     public synchronized AWTEvent getNextEvent()

     public synchronized AWTEvent peekEvent()

     public synchronized AWTEvent peekEvent(int eventID)

It would obviously be a security problem to allow untrusted applets to freely manipulate the system event queue, therefore the getEventQueue() method is protected by a SecurityManager check which disallows untrusted applets direct access to the queue. However, we realize that applets often have a need to look in the queue (particularly if they wish to implement compression of mouse events), so we are providing appropriate Applet methods for peeking at the queue, with the restriction that Applets will only "see" events contained on components within their own containment hierarchy.


Compatibility with the old Model

Our intention is to maintain binary compatibility for programs written to the old model for the 1.1 release. However, we strongly recommend new java programs migrate to the new model. We do not support the explicit mixing of the two models and cannot guarantee predictable results if programs attempt to use both models within a single Java program.

The way the AWT distinguishes between the old and the new models is by checking whether or not a listener object is registered on the component. If one is, then ONLY the new model will be supported on that component. If one is not, then the old style events will be delivered to the handleEvent() method as before. Since the old model supports the propagation of events up the component hierarchy, then it's possible that an old style event will get propagated to a component which has a listener registered; in this case, the event propagation stops and treats this latter component as if it was the root of the hierarchy. In general we DO NOT recommend that programs mix the two event models as it can obviously create some very complex distribution scenarios which could be difficult to debug.

If we discover during initial implementation and testing that the above policy is too complex to support, we may elect to move to a more strict model where an Applet can ONLY use one or the other model.



Send feedback to: java-awt@java.sun.com
Copyright © 1996, Sun Microsystems, Inc. All rights reserved.