Tuesday, August 30, 2011

Java: Events, Event Sources and Event Listeners

It is common to hear the terms "events" and "event listeners" within the scope of object oriented programming languages like Java. The terms refer to the observer design pattern, where there exists objects that are observable and objects that observe.

To implement this, what we want to do is have objects respond to events that other objects generate, we require the object that is the source of the event to send a message to objects that are listening for events. The objects that listen for events are called "event listeners". The objects that generate events are "event sources". The message that is sent from event sources to event listeners are represented by event objects that are simply called "events".

We can have multiple event listeners listen to one or more event sources. In this example we will only use one event source.

To start, we must be able to register a disparate number of event listeners with one event source. The best way to do this is to use polymorphism. We create a listener interface that will allow us to view any type of object who's class implements the listener interface as an event listener. Because we can't have multiple inheritance in Java, using an interface leaves us free to inherit from another class, if we choose to do so.

Here we define our listener interface. All event listeners in Java should extend java.util.EventListener.




package com.areaofthoughts.FireListeners;

import java.util.EventListener;

/**
 * Copyright 2011 AreaOfThoughts.com
 * @author Mark
 */
// Tag the interface as an EventListener by extending the EventListener 
// interface.
public interface FireListener extends EventListener {
    
    void notification(FireEvent fe);
 
}

Now we can create an event listener class who's objects responds to an event. In this case the event will be starting a fire.We implement the FireListener interface that we just created. Our event listener will be a person responding to the fire with a message of concern.

package com.areaofthoughts.FireListeners;

/**
 * Copyright 2011 AreaOfThoughts.com
 * @author Mark
 */
public class Alice implements FireListener {
    @Override
    public void notification(FireEvent fe) {
        System.out.println(this.toString() + " : Eeeeek! A fire!");
    }
   
    @Override
    public String toString() {
        return "Alice";
    }
}

We will create four more event listener classes named Elizabeth, Elvira, Mark and Shirley with identical code, but with different response messages. I will not show the code for those since they are nearly identical to Alice.

Next we create an event source class. The object of this class will accept registrations from people (event listeners) that are listening for fire events.

package com.areaofthoughts.FireListeners;

import java.util.ArrayList;

/**
 * Copyright 2011 AreaOfThoughts.com
 * @author Mark
 */
public class FireEventSource {
    
    private FireEvent fireEvent;
    
    // Initialization Block
    {
        fireEvent = new FireEvent(this, "start fire", System.currentTimeMillis());
    }
    
    // Container for subscribed FireListeners.
    ArrayList<FireListener> fireListenerList = new ArrayList<>();
    
    // Add FireListeners.
    public void addFireListener(FireListener fl) {
        fireListenerList.add(fl);        
    }
    // Remove FireListeners.
    public void removeFireListener(FireListener fl) {
        fireListenerList.remove(fl);
    }
    // Fire event notifications to FireListeners.
    public void startFire() {
        System.out.println("Fire Started in " + fireEvent.getSource().getClass()
                .getSimpleName() + "!");
        for(FireListener obj : fireListenerList) {
                obj.notification(fireEvent);
        }
    }
    
}

Now we will create our event class. The event class' objects are the messages that are sent from the event source to the event listeners. In our case, the event is a "fire event" and contains information about where the fire started, the command used to start it and the time it happened. In our case we our only interested in the event source.

All events in Java should extend java.util.EventObject. 

package com.areaofthoughts.FireListeners;

import java.util.EventObject;

/**
 * Copyright 2011 AreaOfThoughts.com
 * @author Mark
 */
public class FireEvent extends EventObject {
    
    private String actionCommand;
    private long when; 
        
    public FireEvent(Object source, String command, long when) {
        super(source);
        this.actionCommand = command;
        this.when = when;        
    }
    
    public String getActionCommand() {
        return actionCommand;
    }
    
    public long getWhen() {
        return when;
    }
    
    @Override
    public String toString() {
        return this.getClass().getName() + "[command=" + actionCommand 
                + ",when=" + when + "] on " + source;
    }
}

We then create a Main class that has our main entry point method. An object of the Main class will create fire listeners and register the fire listeners with our event source. The Main object will also create fires and will trigger our event source to notify its listeners.

package com.areaofthoughts.FireListeners;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Copyright 2011 AreaOfThoughts.com
 * @author Mark
 */
public class Main {
    
    private FireEventSource fireEventSource;
                
    // Initialization Block
    { 
        // Create a new FireEventSource object where our event will take place.
        fireEventSource = new FireEventSource();
        
        // Create new FireListeners and add them to container.
        fireEventSource.addFireListener(new Alice());
        fireEventSource.addFireListener(new Elizabeth());
        fireEventSource.addFireListener(new Elvira());
        fireEventSource.addFireListener(new Mark());
        fireEventSource.addFireListener(new Shirley());
    }
    
    public static void main(String[] args) {
        Main mainMain = new Main();
        mainMain.go();
    }
   
    private void go() {
        BufferedReader userInput = 
                new BufferedReader(new InputStreamReader(System.in));
        String input;
        while(true) {
            try {
                // If user enters "fire", a fire starts.
                System.out.println("Enter \"fire\" to start a fire or 
                        \"Q\" to quit.");
                input = userInput.readLine().toLowerCase();
                switch (input) {
                    case "fire":
                        // Start fire.
                        fireEventSource.startFire(); 
                        break;
                    case "q":
                        // A zero status indicates normal termination.
                        System.exit(0); 
                }
            } catch (IOException ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

Ouput:
Enter "fire" to start a fire or "Q" to quit.
fire
Fire Started in FireEventSource!
Alice : Eeeeek! A fire!
Elizabeth : Somebody call 911!
Elvira : Oh no, there's a fire!
Mark : Grab your undies, there's a fire!
Shirley : Turn the stove off!
Enter "fire" to start a fire or "Q" to quit.
q

5 comments:

  1. Finally i found a great example! Thank you Mark.

    ReplyDelete
  2. Thank you for explaining event , event source and event listener ....

    ReplyDelete
  3. The Alice class should read "public void notification(FireEvent fe)" instead of "public void notification()".

    ReplyDelete
  4. Thank you for taking the time to publish this information very useful! event sponsorship

    ReplyDelete