CS 211, 004/006
Lab Exercise-12
due Sunday, December 2nd at 11:59pm


In this lab, we demonstrate a way in which signals are sent in Java objects. One class produces signals, and other classes, known as listeners, recieve the signals. We will implement a signal sender and a signal listener. The listeners will be generic (we can make listeners which accept data of any data type). Additionally, we will make sure that listeners are inserted in sorted order.

Overview:

  1. Create a generic SignalListener interface which recieves signals.
  2. Create the generic SignalFetcher class which implements SignalListener and prints a message whenever a signal is received.
  3. Create the generic SignalSender class which lets you attach any number of SignalListener objects and send a signal to all of them; furthermore, when the signal is sent to each of the listeners, it is done in order based on a key value.
This is a lab exercise on which collaboration (including discussing the solution on Piazza or searching online sources) is explicitly permitted. It is still in your interest to understand and prepare your final submission yourself.

Background:

There are often cases when you need to send signals from one object to another. For example, if you click on a button in your user interface, some other part of your code may react to it. Or, if you are monitoring a sensor and its battery power is approaching zero, you may want to send an alert. This is often done by noting the parts of your code which are capable of generating signals (also known as events), and registering other objects as listeners for these signals. Whenever a signal occurs, the originating object sends the signal to any other objects which happen to be listening at the time.

The signalling process has two parts: the signal generating object, and the signal listening object. The generating object would typically have a method to add (and remove) listeners. Listeners are usually added to an internal list (possibly an ArrayList), and whenever a signal is generated, the signal generator simply goes through and calls a method to send the signal to each of the listener objects. Since many different types of classes might be interested in receiving a signal, the signal listener is typically just represented as an interface which is implemented by any interested classes.

When we send a signal, what does the signal look like? That depends. We may be sending something as simple as a numerical reading, or we have some kind of event object (like a mouse click which has coordinates of the click and information about which button was used). If we don't know in advance, it may make sense to use generics. This allows us to leave the type unspecified until we need it.

Another question is whether it makes a difference which order the listeners are called. Maybe it doesn't make a difference, in which case first-come-first-served would be good enough. But if it does make a difference, then it may make sense to include some key which shows the precedence between different listeners.

In this lab, we will build a signal generator and a signal listener, and we will do it with generics so that it can be applied to any signal type we want to send. Furthermore, we will make sure to sort the list of listeners as we add them by priority, so that we can ensure the order in which they are called.

Task:

First, let's create a listener interface. We will call this interface SignalListener. The SignalListener will have two generic type parameters associated with it: the first is the type for the key, which is used to prioritize listeners when they're added to the signal generator. The second is the type of the object which is sent over whenever a signal is sent. So if we were to declare the variable SignalListener<Integer, String> listener, then listener is prioritized by an Integer key and the signals which are sent to it are Strings. The interface should have two methods: a getKey() call which returns the key value; and a signal(signalvalue) call which does not return anything, and takes a single parameter of the signal object's type.

Second, we will implement the SignalListener interface in the SignalFetcher class, which also has two generic type parameters. This class's primary purpose is to print messages whenever a signal is recieved. It must have: a one-parameter constructor which allows us to initialize the key value; the getKey() method required by the interface; a toString() method which gives the word "listener" followed by a space followed by the toString() value of the key; and the signal message, which prints a message in the following format whenever it receives a signal:

listener key's_toString() received signal signalvalue's_toString()

Finally, we would like to have a signal generator class, which we shall call SignalSender. Like the other classes and interfaces in this project, the SignalSender is generic and has two generic type parameters, the key type and the signal type. This class will allow you to add new listeners, which will put them in a list. Also, it will have a method to send a signal to all registered listeners (all listeners which are in the list). We want the signal to be sent to each listener in order by the signal's key value, which means that the listeners should be sorted at some point. Any sorting algorithm (or built-in sorting method) may be used for this. The methods which you are responsible for are: a default constructor (no parameters); an addSignalListener(listener) method which takes an appropriate SignalListener object as a parameter (and does not return anything), and adds the listener to the list; and a sendSignal(signalvalue) method which sends the given signal to all registered listeners, in order by key.

Tip: since the signal listeners must be in order by key, it would make sense to require the key to be some kind of Comparable type; this can be enforced within the declaration of the generic type parameters.

Example:

> SignalSender<Integer,String> generator = new SignalSender<Integer,String>();
> SignalListener<Integer,String> listener1 = new SignalFetcher<Integer,String>(1);
> SignalListener<Integer,String> listener2 = new SignalFetcher<Integer,String>(2);
> SignalListener<Integer,String> listener3 = new SignalFetcher<Integer,String>(3);
> generator.addSignalListener(listener3);
> generator.addSignalListener(listener1);
> generator.addSignalListener(listener2);
> generator.sendSignal("hello");
listener 1 received signal hello
listener 2 received signal hello
listener 3 received signal hello

Testing:

The tester can be downloaded from:

Submission:

Submission instructions are as follows.

  1. Let xxx be your lab section number (one of 213-220), and let yyyyyyyy be your GMU userid. Create the directory xxx_yyyyyyyy_E12/
  2. Place your three .java files into the directory that you've created.
  3. Create the file ID.txt in the format shown below, containing your name, userid, G#, lecture section and lab section, and add it to the directory.

    Full Name: Donald Knuth
    userID: dknuth
    G#: 00123456
    Lecture section: 004
    Lab section: 213

  4. compress the folder and its contents into a .zip file, and upload the file to Blackboard.