Your lab team will experiment with one particular communication middleware package, Java RMI. You will do this on the general-access MCS lab computers, not the ones you have been using for kernel-level experimentation.
You will be fusing together two example programs from two different textbooks: the sliding 15-tile puzzle from Chapter 15 of Concrete Abstractions and the Publisher/Topic/Subscriber example RMI system from Chapter 10 of the operating systems book. In this way, you will be able to produce a multi-user puzzle. (As of this writing, the multiple users will need to all be running on one computer, as the MCS machines are unable to communicate with each other for RMI. Hopefully this will be resolved before the lab starts.)
Before you write any code of your own, you should make sure you can compile and run the two textbooks' code.
For the puzzle program from Concrete Abstractions, you
will need to download Puzzle.java
and TileActionListener.java
. Of these,
the main Puzzle.java
program is a slight adaptation of the first
version shown in the book: it was modified to run as a stand-alone
application rather than as an applet in a web browser. The supporting
file TileActionListener.java
is copied verbatim from the book. (The
full text of this book, by Max Hailperin, Barbara Kaiser, and Karl Knight, is now
available for free from the web
page.) You should be able to compile the two files using
javac
and then run java Puzzle
; if all is
well, a window will pop up in which you can click on the buttons to
move the tiles. If multiple copies of the Puzzle
are run, they will
each function independently, as no communication is occurring.
For the RMI example, you will need to download all five files
listed under Chapter 10 on the web
site of programs from the textbook. Compile these using
javac
. Then run rmiregistry&
to start a
registry going and java TopicServer
to register a server
with it. In other windows you should now be able to use java
Subscriber
and java Publisher
someMessage in order to communicate.
Your primary goal for the lab is to merge into the Puzzle
and
TileActionListener
classes some code like that in the Subscriber
and
Publisher
classes, so that multiple instances of the puzzle will
communicate. In particular, every time a user clicks one of the
buttons on one of the puzzles, it will publish a message describing
which button was clicked. All the puzzles (itself included) will
receive this message as subscribers and will respond to the pushed tile.
There are two possible approaches you can take for the messages
sent between the puzzles (by way of the TopicServer
):
You could create a TilePosition
class that
contains two integers, a row number and a column number. Instances of
this class would be sent between the puzzles to indicate which button
was clicked. This will require changing the
MessageRecipient
and Topic
interfaces and
the TopicServer
class to use TilePosition
in
place of String
for the messages. In order to be
conveyed using RMI, the TilePosition
class will need to
implement the interface java.io.Serializable
.
You could communicate using String
s, which would
allow the RMI code from the textbook to be unchanged. When the puzzle
wants to publish a message describing the row and column that was
clicked, it can join them together into a String
, with a
space in between, using code like row + " " + col
. When
the puzzle (in its role as subscriber) receives a message, it will
need to read the two integers out of the String
; the
easiest way to do this is using the class java.util.Scanner.
The first approach will be slightly more efficient, as it does not
need to translate the integers into decimal digits and back. The
second approach has the advantage that the Subscriber
and
Publisher
classes can be used to eavesdrop on, and butt
into, the puzzles' conversation, which is handy for debugging.
When you have used either approach to integrate RMI communication
into the puzzle, you should be able to run several instances of
java Puzzle
and find that they all operate in tandem;
clicking on any one affects them all. (This assumes you have the
rmiregistry
and TopicServer
running.)
Your code from the prior part of the lab should work well as long as all the puzzles start running before any user clicks on any of their buttons. However, if after clicking some buttons to rearrange the tiles, you start another copy of the puzzle running, you will see that it is out of synch with the others. Its tiles are in the initial configuration, unlike the tiles in the other puzzles. Clicking on buttons in either the new puzzle or one of the old ones will affect the others, but since the arrangements of tiles start out different, they will continue to differ.
The simplest (if not best) solution to this problem is to replace
the TopicServer
with a
TopicServerWithHistory
. This new class should implement
the same Topic
interface, so you should be able to plug
it in without changing anything else. The difference is that it keeps
a history of all the published messages and whenever a new subscriber
is added sends all of those previous messages to the new
subscriber.
Write this class and demonstrate that when a puzzle is started, its tiles get pushed around to match the preexisting puzzles.
Your report should consist of printouts of your code for each Java file that you wrote or modified. You need not turn in anything other than this code. Be sure that you include an honor pledge signed by all co-authors.
If you are looking for additional excitement, I can think of many
possible extensions to the lab. I list here just some examples. If
multiple users are cooperating on a puzzle across the network, they
likely would want to send textual messages back and forth as well, to
coordinate their work; you could build a messaging facility into the
puzzle. Accommodating late-comers by keeping a full history of past
messages is inefficient; you could achieve the same goal in a
different way. Right now, one sluggish subscriber can hold everybody
up; you could modify the TopicServer
to create a new
thread for each message delivery, or you could use a pool of threads
to achieve the same effect without so many thread creations.