JavaBeans Event Model

0

In this article, I will show an example of how events are sent from one object to another. By the end of the article, we will know about the Java Event Model and event notification-listener mechanism.

Events

The component sending the event is called the event source. On the occurrence of an event, it fires the event and notifies all the registered listeners about the event occurred.
The component receiving the event is called a listener, its job is to handle the event.
There can be more than one listener listening for an event and there can be more than one event object that you would like to fire.

Event Object

When an event occurs, the application creates an EventObject. You can either use it directly or extend EventObject and add more state information to it. Once the event object is created, it notifies the listeners by invoking a method on the listening object and passing on the event object as an argument.
You can either call a generic method like handleNodeEvent(event) or a specific event method like onNodeUnlinked(event).

Event Source

Event sources are objects that fire events. These objects implement methods which allows the listener to register and unregister.
The event object passed as argument contains specific information about that particular instance of the event like the event type. The event will always contain a reference to the object that was the source of the event. By convention, method used for registration should start with add<ListenerType>Listener() and likewise for unregistering, the pattern is remove<ListenerType>Listener().
An example would be addNodeEventListener() and removeNodeEventListener().

EventListener

An event listener is an object that would like to be notified when certain events occur. Any class that wants to receive notifications of the event would implement the interface EventListener

package java.util;
public interface EventListener {
}

By convention, all event listeners have names ending in the word Listener

Java Event Model

The Java event model consists of event objects, event listeners and event sources.
Java package java.util provides basic support for the event model.
It provides basic class for event objects called EventObject and a market interface for the listener EventListener.

Now I will show an example that implements Java Event Model. My example consists of two nodes that can be linked and unlinked. Node class acts as an event source here.
I will show you the implementation of link() and unlink methods when I reach the notification section. You can download the source code here:

public class Node {
    private Object value;
    private Node next;
    private Node previous;

    public Node(Object value) {
        this.value = value;
    }

    public Object getValue() {
        return value;
    }

    public Node getNext() {
        return next;
    }

    public Node getPrevious() {
        return previous;
    }

    public void link(Node node) {
    //link implementation
    }
    
    public void unlink() {
    //unlink implementation
    }
}

Here is my event object. The link node event will have references to the nodes linked and the unlink node event will have the reference to the node unlinked.

import java.util.EventObject;

public class NodeEvent extends EventObject {

    public NodeEvent(Object source, Node node, NodeEventState eventState) {
        super(source);
        node2 = node;
        mNodeEventState = eventState;
    }

    public NodeEventState getNodeEventState() {
        return mNodeEventState;
    }

    public Node getNode1() {
        return  (Node) getSource();
    }

    public Node getNode2() {
        return node2;
    }

    public String toString() {
        return getNode1() + "-" + getNode2() + "-" + getNodeEventState();
    }
    
    private Node node2;
    private NodeEventState mNodeEventState;
    private static final long serialVersionUID = 1L;

}

NodeEventListener will implement EventListener. It can either have a generic method like handleEvent() or you can have specific methods for each event like OnNodeUnlinked()

import java.util.EventListener;

public interface NodeEventListener extends EventListener {
    //example of generic API
    void handleNodeEvent(NodeEvent nodeEvent);
    //example of specific API
    void onNodeUnlinked(NodeEvent nodeEvent);
}

Add and remove Listener

Node will have the below addNodeEventListener and removeNodeEventListener methods to add and remove listeners. Notice that I am creating a new array whenever a new listener is being added, so that any notification happening concurrently is least affected. You may also use CopyOnWriteArrayList

   public void addNodeEventListener(NodeEventListener listener) {
        if (listeners == null) {
            listeners = new NodeEventListener[] { listener };
        } else {
            NodeEventListener results[] = new NodeEventListener[listeners.length + 1];
            for (int i = 0; i < listeners.length; i++)
                results[i]= listeners[i];
            results[listeners.length] = listener;
            listeners = results;
        }
    }
    public void removeNodeEventListener(NodeEventListener listener) {
        int n = -1;
        for (int i = 0; i < listeners.length; i++) {
            if (listeners[i]== listener) {
                n = i;
                break;
            }
        }
        if (n < 0)
            return;
        NodeEventListener results[] = new NodeEventListener[listeners.length - 1];
        int j = 0;
        for (int i = 0; i < listeners.length; i++) {
            if (i != n)
                results[j++] = listeners[i];
        }
        listeners = results;
    }

Notify listeners

The notification mechanism uses standard methods. The link methods notifies the listeners before and after the nodes are linked.

    public void link(Node node) {
        notifyListeners(node, NodeEventState.PRE_LINK);
        node.previous = this;
        node.next = next;
        if (next != null) {
            node.next.previous = node;
        }
        next = node;
        notifyListeners(node, NodeEventState.AFTER_LINK);
    }
    private void notifyListeners(Node node, NodeEventState nodeState) {
        NodeEvent event = new NodeEvent(this, node, nodeState);
        NodeEventListener interested[] = listeners;
        for (int i = 0; i < interested.length; i++)
            interested[i].onEvent(event);
    }

You can either use generic notification method or a specific method like the Unlink method which calls the specific notification method which is meant just for unlink event.

    public void unlink() {
        if (next != null) {
            next.previous = previous;
        }
        if (previous != null) {
            previous.next = next;
        }   
        notifyListenersNodeUnlinked();
    }


    private void notifyListenersNodeUnlinked() {
        NodeEvent event = new NodeEvent(this, NodeEventState.UNLINK);
        NodeEventListener interested[] = listeners;
        for (int i = 0; i < interested.length; i++)
            interested[i].onNodeUnlinked(event);       
        
    }

In the below example, I am adding two event listeners to the node. Each event listener prints the node event received.
I then link node1 to node2. Next, I remove the second listener and again link node1 to node3. Finally, I unlink node1.

import junit.framework.TestCase;

public class JavaBeansTest extends TestCase {
    public void testNodeListeners() {
        final Node node1 = new Node(1);
        final Node node2 = new Node(2);
        NodeEventListener nodeEventListener1 = new NodeListener1();
        NodeEventListener nodeEventListener2 = new NodeListener2();
        node1.addNodeEventListener(nodeEventListener1);
        node1.addNodeEventListener(nodeEventListener2);        
        node1.link(node2); 
        System.out.println("remove node event listener2");
        node1.removeNodeEventListener(nodeEventListener2);
        node1.link(new Node(3));
    }
    
    private class NodeListener1 implements NodeEventListener {
        @Override
        public void onEvent(NodeEvent nodeEvent) {
            System.out.println("Listener 1 receives " + nodeEvent + " event");  
        }
    }
    
    private class NodeListener2 implements NodeEventListener {
        @Override
        public void onEvent(NodeEvent nodeEvent) {
            System.out.println("Listener 2 receives " + nodeEvent + " event");  
        }
    }

Output:
We can see the listeners getting the notification. After the removal of second listener, only the first listener gets the notification.
In the end, listener1 receives the ‘Node Un-linked’ event.

Listener 1 receives node1-node2-PRE_LINK event
Listener 2 receives node1-node2-PRE_LINK event
Listener 1 receives node1-node2-AFTER_LINK event
Listener 2 receives node1-node2-AFTER_LINK event
remove node event listener2
Listener 1 receives node1-node3-PRE_LINK event
Listener 1 receives node1-node3-AFTER_LINK event
Unlinked: Listener 1 receives node1-UNLINK event

Download the code javabeans.zip

Share.

Leave A Reply