11.3 Announcer Listener


11.3 Announcer – Listener

Announcer – Listener is an example of an event-based architecture. The announcer process announces that some event has occurred and disseminates it to all those listener processes that are interested in the event. The communication pattern is one (announcer) to zero or more (listeners). Listener processes indicate their interest in a particular event by registering for that event. In the architecture diagram of Figure 11.14, we have termed the connector that handles event dissemination an “event manager”.

image from book
Figure 11.14: Announcer – Listener process architecture.

Listeners can choose to receive only a subset of the events announced by registering a “pattern” with the event manager. Only events that match the pattern are forwarded to the listener. The architecture can be applied recursively so that listeners also announce events to another set of listeners. In this way, an event dissemination “tree” can be constructed.

An important property of this architecture is that the announcer is insulated from knowledge of how many listeners there are and from which listeners are affected by a particular event. Listeners do not have to be processes; they may simply be objects in which a method is invoked as a result of an event. This mechanism is sometimes called implicit invocation since the announcer does not invoke listener methods explicitly. Listener methods are invoked implicitly as a result of an event announcement.

The Announcer – Listener architecture is widely used in user interface frameworks, and the Java Abstract Windowing Toolkit (AWT) is no exception. In section 11.3.2, we use the AWT event mechanism in an example program. In AWT, listeners are usually ordinary objects. Events, such as mouse clicks and button presses, cause methods to be invoked on objects. Our example uses events to control the execution of thread objects.

11.3.1 Announcer – Listener Model

The model is defined for a fixed set of listeners and a fixed set of event patterns:

 set Listeners = {a,b,c,d} set Pattern   = {pat1,pat2}

The event manager is modeled by a set of REGISTER processes, each of which controls the registration and event propagation for a single, particular listener.

 REGISTER = IDLE, IDLE = (register[p:Pattern] -> MATCH[p]        |announce[Pattern]   -> IDLE        ), MATCH[p:Pattern] =        (announce[a:Pattern] ->            if (a==p) then               (event[a] -> MATCH[p]               |deregister -> IDLE)            else               MATCH[p]        |deregister -> IDLE        ). ||EVENTMANAGER = (Listeners:REGISTER)                /{announce/Listeners.announce}.

The REGISTER process ensures that the event action for a listener only occurs if the listener has previously registered, has not yet unregistered, and if the event pattern matches the pattern with which the listener registered. Figure 11.15 depicts the LTS for a:REGISTER for listener a.

image from book
Figure 11.15: LTS for a:REGISTER.

The announcer is modeled as repeatedly announcing an event for one of the patterns defined by the Pattern set:

 ANNOUNCER = (announce[Pattern] -> ANNOUNCER).

The listener initially registers for events of a particular pattern and then either performs local computation, modeled by the action compute, or receives an event. On receiving an event, the process either continues computing or deregisters and stops.

 LISTENER(P='pattern) =   (register[P] -> LISTENING), LISTENING =   (compute  -> LISTENING   |event[P] -> LISTENING   |event[P] -> deregister -> STOP   )+{register[Pattern]}.

ANNOUNCER_LISTENER describes a system with four listeners a, b, c, d, in which a and c register for events with pattern pat1 and b and d register for pat2.

 ||ANNOUNCER_LISTENER            = ( a:LISTENER('pat1)              ||b:LISTENER('pat2)              ||c:LISTENER('pat1)              ||d:LISTENER('pat2)              ||EVENTMANAGER              ||ANNOUNCER              ||Listeners:SAFE).

Analysis

The safety property, SAFE, included in the composite process ANNOUNCER_LISTENER, asserts that each listener only receives events while it is registered and only those events with the pattern for which it registered. The property is defined below:

 property   SAFE = (register[p:Pattern]  -> SAFE[p]),   SAFE[p:Pattern]= (event[p]   -> SAFE[p]                    |deregister -> SAFE                    ).

Safety analysis reveals that the system has no deadlocks or safety violations. A sample execution trace is shown in Figure 11.16.

image from book
Figure 11.16: ANNOUNCER_LISTENER trace.

The important progress property for this system is that the announcer should be able to announce events independently of the state of listeners, i.e. whether or not listeners are registered and whether or not listeners have stopped. We can assert this using the following set of progress properties:

 progress ANNOUNCE[p:Pattern] = {announce[p]}

Progress analysis using LTSA verifies that these properties hold for ANNOUNCER_LISTENER.

11.3.2 Announcer – Listener Implementation

To illustrate the use of the Announcer – Listener architecture, we implement the simple game depicted in Figure 11.17. The objective of the game is to hit all the moving colored blocks with the minimum number of mouse presses. A moving block is hit by pressing the mouse button when the mouse pointer is on top of the block. When a block is hit, it turns black and stops moving.

image from book
Figure 11.17: EventDemo applet display.

Each block is controlled by a separate thread that causes the block it controls to jump about the display at random. The threads also listen for mouse events that are announced by the display canvas in which the blocks move. These events are generated by the AWT and the program uses the AWT classes provided for event handling. The class diagram for the program is depicted in Figure 11.18.

image from book
Figure 11.18: EventDemo class diagram.

The display for the EventDemo applet is provided by the BoxCanvas class described in outline by Program 11.10.

Program 11.10: BoxCanvas class.

image from book
 class BoxCanvas extends Canvas {   // clear all boxes   synchronized void reset(){...}   // draw colored box id at position x,y   synchronized void moveBox(int id, int x, int y){...}   // draw black box id at position x,y   synchronized void blackBox(int id, int x, int y){...} }
image from book

The AWT interface MouseListener describes a set of methods that are invoked as a result of mouse actions. To avoid implementing all these methods, since we are only interested in the mouse-pressed event, we use the adapter class MouseAdapter which provides null implementations for all the methods in the MouseListener interface. MyClass extends MouseAdapter and provides an implementation for the mousePressed method. MyClass is an inner class of the BoxMover thread class; consequently, it can directly call private BoxMover methods. The code for BoxMover and MyListener is listed in Program 11.11.

Program 11.11: BoxMover and MyListener classes.

image from book
 import java.awt.event.*; class BoxMover extends Thread {   private BoxCanvas display;   private int id, delay, MaxX, MaxY, x, y;   private boolean hit=false;   private MyListener listener = new MyListener();   BoxMover(BoxCanvas d, int id, int delay) {     display = d; this.id = id; this.delay=delay;     display.addMouseListener(listener); // register     MaxX = d.getSize().width; MaxY = d.getSize().height;   }   private synchronized void isHit(int mx, int my) {     hit = (mx>x && mx<x+display.BOXSIZE         && my>y && my<y+display.BOXSIZE);     if (hit) notify();   }   private synchronized boolean waitHit()     throws InterruptedException {     wait(delay);     return hit;   }   public void run() {     try {       while (true) {           x = (int)(MaxX*Math.random());           y = (int)(MaxY*Math.random());           display.moveBox(id,x,y);           if (waitHit()) break;       }     } catch (InterruptedException e){}     display.blackBox(id,x,y);     display.removeMouseListener(listener); // deregister   }   class MyListener extends MouseAdapter {     public void mousePressed(MouseEvent e) {       isHit(e.getX(),e.getY());     }   } }
image from book

The run() method of BoxMover repeatedly computes a random position and displays a box at that position. After displaying the box, waitHit() is called. This uses a timed wait() and returns for two reasons: either the wait delay expires and the value false is returned or isHit() computes that a hit has occurred and calls notify() in which case true is returned. Because MyListener is registered as a listener for events generated by the display, whenever the display announces a mouse-pressed event, MyListener.mousePressed() is invoked. This in turn calls isHit() with the mouse coordinates contained in the MouseEvent parameter.

If we compare the run() method with the LISTENER process from the model, the difference in behavior is the addition of a timeout action which ensures that after a delay, if no mouse event occurs, a new box position is computed and displayed. A model specific to the BoxMover thread is described by:

 BOXMOVER(P='pattern) =   (register[P] -> LISTENING), LISTENING =   (compute->       // compute and display position     (timeout -> LISTENING    // no mouse event     |event[P] -> timeout -> LISTENING   // miss     |event[P] -> deregister -> STOP      // hit     )   )+{register[Pattern]}.

The reader should verify that the safety and progress properties for the ANNOUNCER_LISTENER still hold when BOXMOVER is substituted for LISTENER.




Concurrency(c) State Models & Java Programs
Concurrency: State Models and Java Programs
ISBN: 0470093552
EAN: 2147483647
Year: 2004
Pages: 162

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net