Some reader favorites:
EJB fundamentals and session beans
Create a scrollable virtual desktop in Swing
More action with Struts 2
In a recent review of Struts 2 in Action, JW Blogger Oleg Mikheev notes that Struts 2 is "just a collection of extensions built upon WebWork, which is ultimately
the right thing to learn before starting a Struts 2 project." While Struts 2 has some architectural flaws, Oleg calls WebWork
well-designed, well-tested, and reliable. What are your experiences using Struts 2 and WebWork?
Also see "Hello World the WebWork way," a JavaWorld excerpt from WebWork in Action, by Patrick Lightbody and Jason Carreira.
| Memory Analysis in Eclipse |
| Enterprise AJAX - Transcend the Hype |
March 15, 2002
n "Events and Listeners," I outlined a generic recipe for creating and firing custom events. Since the original answer published, I've received several
emails requesting a complete, concrete example.
In the original Java Q&A, I specified that you need three elements to generate and listen to custom events:
Let's quickly review each.
Note: You can download this article's source code from Resources.
Any class can fit an event's role. At a minimum, an event should provide a method that allows the listener to retrieve the event's source.
Java includes a few built-in super types for creating events. Have a look at:
Normally you extend AWTEvent for events generated by a graphical component and EventObject any other time. If you do use one of these super types, you merely extend it and provide the methods unique to your custom
event.
When creating an event class, you must guarantee that the event is immutable. The event generator will share the same event instance among the listeners; so ensure any one listener cannot change the event's state.
The listener interface provides the contract between the listeners and the event generator. The contract provides the event generator with the method to call when it fires an event.
When creating an event listener interface, you can add as many methods as you need. However, by convention, each method normally takes only one argument: the event.
An event generator tracks listeners, provides a mechanism to add and remove listeners, and, at the appropriate time, fires events to the listeners.
When creating an event generator, make sure its registration mechanism is thread safe. Beyond that, writing an event generator class closely resembles writing any other class.
In this article, I bring back MrHappyObject first seen in "Client Callbacks."

Figure 1. Mr. Happy Object
In the original example, Mr. Happy Object became either happy, annoyed, or angry based on receiving hugs or pinches. In this
example, we'll change how we learn about MrHappyObject's mood. Instead of calling a method, we'll register listeners with MrHappyObject. Whenever he changes his mood, MrHappyObject will send an event to his listeners.
Let's start by defining the new MoodEvent and some Mood constants.
MrHappyObject has three moods: happy, annoyed, and angry. When he fires an event, the listener will retrieve a mood. So, let's first define
some mood constants:
public class Mood {
public static final Mood HAPPY = new Mood( "happy" );
public static final Mood ANNOYED = new Mood( "annoyed" );
public static final Mood ANGRY = new Mood( "angry" );
private String _mood;
public String toString() {
return _mood;
}
private Mood( String mood ) {
_mood = mood;
}
}
While we could have made do with some string constants, this approach is a little cleaner, in my opinion.
Next, let's consider the MoodEvent class:
import java.util.EventObject;
public class MoodEvent extends EventObject {
private Mood _mood;
public MoodEvent( Object source, Mood mood ) {
super( source );
_mood = mood;
}
public Mood mood() {
return _mood;
}
}
MoodEvent holds on to the source, as well as one of the Mood constants. When a listener receives the event, it can retrieve the mood by calling the mood() method.
Next, we need a listener interface so MrHappyObject can inform others of his mood:
public interface MoodListener
{
public void moodReceived( MoodEvent event );
}
When MrHappyObject changes his mood, he will call the moodReceived() method on each of his listeners.
Now, let's look at the MrHappyObject class:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class MrHappyObject {
private Mood _mood = Mood.HAPPY;
private List _listeners = new ArrayList();
public synchronized void receivePinch() {
if( _mood == Mood.HAPPY ) {
_mood = Mood.ANNOYED;
_fireMoodEvent();
} else {
_mood = Mood.ANGRY;
_fireMoodEvent();
}
}
public synchronized void receiveHug() {
if( _mood == Mood.ANGRY ) {
_mood = Mood.ANNOYED;
_fireMoodEvent();
} else {
_mood = Mood.HAPPY;
_fireMoodEvent();
}
}
public synchronized void addMoodListener( MoodListener l ) {
_listeners.add( l );
}
public synchronized void removeMoodListener( MoodListener l ) {
_listeners.remove( l );
}
private synchronized void _fireMoodEvent() {
MoodEvent mood = new MoodEvent( this, _mood );
Iterator listeners = _listeners.iterator();
while( listeners.hasNext() ) {
( (MoodListener) listeners.next() ).moodReceived( mood );
}
}
}
Of special interest are the following methods:
public synchronized void addMoodListener( MoodListener l ) {
_listeners.add( l );
}
public synchronized void removeMoodListener( MoodListener l ) {
_listeners.remove( l );
}
private synchronized void _fireMoodEvent() {
MoodEvent mood = new MoodEvent( this, _mood );
Iterator listeners = _listeners.iterator();
while( listeners.hasNext() ) {
( (MoodListener) listeners.next() ).moodReceived( mood );
}
}
With addMoodListener() and removeMoodListener(), listeners can register and deregister their interest in events. Note: Because these methods are synchronized, you've guaranteed
the listener list cannot change while firing the event in _fireMoodEvent().
_fireMoodEvent() simply creates an event indicating the mood, then loops through each listener calling moodReceived(). MrHappyObject calls _fireMoodEvent() whenever his mood changes in the receivePinch() and receiveHug() methods.
To complete the example we will create two listeners: Sky and FlockOfBird, each of which responds to MrHappyObject's mood:
public class Sky implements MoodListener {
public void moodReceived(MoodEvent event) {
if( event.mood() == Mood.HAPPY )
{
System.out.println( "Sun is shining!" );
}
else if( event.mood() == Mood.ANNOYED )
{
System.out.println( "Cloudy Skies!" );
}
else
{
System.out.println( "Lightning rains from the heavens!" );
}
}
}
And:
public class FlockOfBirds implements MoodListener {
public void moodReceived(MoodEvent event) {
if( event.mood() == Mood.HAPPY )
{
System.out.println( "Birds are singing!" );
}
else if( event.mood() == Mood.ANNOYED )
{
System.out.println( "Birds are silent!" );
}
else
{
System.out.println( "Birds are flying away!" );
}
}
}
We now have a custom event, a custom event listener interface, an event generator, and some listeners. Let's put it all together:
public class ADayInTheLife {
public static void main( String [] args ) {
MrHappyObject happy = new MrHappyObject();
MoodListener sky = new Sky();
MoodListener birds = new FlockOfBirds();
happy.addMoodListener( sky );
happy.addMoodListener( birds );
System.out.println( "Let's pinch MrHappyObject and
find out what happens:" );
happy.receivePinch();
System.out.println("");
System.out.println( "Let's hug MrHappyObject and
find out what happens:" );
happy.receiveHug();
System.out.println("");
System.out.println( "Now, let's make MrHappyObject
angry and find out what happens:" );
System.out.println("");
System.out.println("one pinch:");
happy.receivePinch();
System.out.println("");
System.out.println("second pinch, look out:");
happy.receivePinch();
}
}
The main() method creates a MrHappyObject instance and the Sky and FlockOfBirds listeners. The main() method registers the listeners, then hugs and pinches MrHappyObject. Upon execution you should see:
Let's pinch MrHappyObject and find out what happens: Cloudy Skies! Birds are silent! Let's hug MrHappyObject and find out what happens: Sun is shining! Birds are singing! Now, let's make MrHappyObject angry and find out what happens: one pinch: Cloudy Skies! Birds are silent! second pinch, look out: Lightning rains from the heavens! Birds are flying away!
Woe to he who angers MrHappyObject!

Figure 2. Mr. Happy Object's joy turns to anger.
(Background photo courtesy of Storm Chasing Adventure Tours.)