AWT event handling
- By Scott Oaks
- February 5, 2001
HOW CAN YOU process all AWT events within an application? There are various reasons why you may want to use the techniques we're about to examine: You may want to log all AWT events, modify events as they are processed, or simply look at events for debugging purposes. We'll look at two different ways this can be achieved.
The first, somewhat obvious, mechanism is to use an object that implements the AWTEventListener interface. Once this object is registered with the toolkit, its eventDispatched() method will be called every time an AWT event is triggered. We could write a simple object to print out all AWT events like this:
public class EventPrinter implements AWTEventListener {
public EventPrinter() {
Toolkit.getDefaultToolkit().
addAWTEventListener(this, ~0L);
}
public void eventDispatched(AWTEvent ae) {
System.out.println(ae);
}
}
The constructor of this class registers the object with the toolkit so that it will receive all the events. When events are received, they are printed in the
eventDispatched() method.
As simple as this is, there are times when this approach is less than ideal. To begin with, we've asked for all AWT events by passing to the addAWTEventListener() method an event mask with every bit set. Nonetheless, it turns out there are certain events we will still not receive. For the most part, these events are internal to the implementation of the VM. Most of them, in fact, are events that are not part of the public Java API. However, there are some public events, such as invocation events triggered by calls to the invokeLater() method, that are not sent to an AWT event listener.
This technique is a passive one, which makes it suitable for logging events, but not for modifying them before they are processed. It also makes it unsuitable if you need to process the event at a particular time. Other components interested in the event may have already received the event by the time you process it, or they may receive the event after you process it.
Finally, this technique is unsuitable in applets or in applications where a security manager is in place, because to call the addAWTEventListener() method, your code must have been granted the listenToAllAWTEvents AWTPermission. Of course, you can modify your security policy to grant your code that permission.
The other approach is to write a class that extends the EventQueue class, and to install an instance of that class into the event queue stack. Then the dispatchEvent() method can be modified to perform whatever event processing is desired. Such a class might look like this:
public class EventProcessor extends EventQueue {
public EventProcessor() {
Toolkit.getDefaultToolkit().
getSystemEventQueue().push(this);
}
protected void dispatchEvent(AWTEvent e) {
doProcess(e);
super.dispatchEvent(e);
}
}
The constructor of this class pushes itself onto the stack of event queues objects. This means this event queue will be in charge of dispatching events. In our example, we call the
doProcess() method (which may print the event, or modify it, or whatever else you may want to do), and then call
super.dispatchEvent(). This last call is important: Even though we can set up a stack of event queues objects, events do not fall through to every event queue object on the stack. It's up to us to make sure this happens.
This event queue will receive all types of events in the VM, even those internal to its implementation. It is an active participant in event dispatching: If we want to swallow an event, we do not call the super.dispatchEvent() method. If an event is modified in the doProcess() method, the modified event is passed to other components. We will process the event before any component receives it (or we could process it after all components have seen it by reversing the order of the two lines of the dispatchEvent() method).
The security implications of this technique are a little complex. If you use this code in an application that installs the default security manager (e.g., by specifying -Djava.security.manager on the command line), then your code needs to be granted the accessEventQueue AWTPermission. However, the appletviewer and the Java Plug-in operate differently. In those programs, applets are essentially given their own event queue. This means they do not need special permission to push the event queue object; you can use the previous code as is within an applet. The trade-off is that applets will see (and be able to modify) only those events associated with their codebase; they will not see events from other applets loaded from other sites.
As usual, the trade-offs associated with each of these techniques will dictate which is more appropriate to use. When you need to do advanced AWT event processing, these techniques are the way to start.
About the Author
Scott Oaks is a Systems Engineer for Sun Microsystems, where he focuses on practical applications of Java Technology. He is the co-author, with Henry Wong, of Java Threads. He can be contacted at [email protected].