|
Homework 3: Implement a Remote Message System
Problem Summary
For this Homework you are required to implement a simplified
Remote Message Send (RMS) Mechanism for Java (or another Object-Oriented
language). This mechanism will be similar in functionality to Java RMI.
I expect most students to do this assignment in Java; it is easiest
in Java because we provide some of the pieces for you, and because the
Java network interfaces are at a higher level than, say, the C interfaces.
However, if you prefer you can choose to use some other object-oriented
language.
Of course, Java already has a RMS mechanism - Java RMI. Java RMI already
contains everything that you are required to produce for this assignment!
The point of the homework is that you implement your own versions
of the Java RMI components, not copy the ones provided by Sun. However,
you are allowed to use other Java libraries, such as those providing socket
communication, "serialization" and "reflection".
The purpose of the assignment is to help you gain a better understanding
of RMS and RPC mechanisms.
Module Overview
This diagram shows the main components in the RMS system that
you will be building.
-
The gray boxes represent classes that are provided for you.
-
The dark gray classes, CSE515Registry
and RegistryClerk together implement
the registry function.
-
The light gray classes, ZipCodeClient and
ZipCodeServerImpl, are samples of typical
client and server classes.
-
The white rectangles represent code that you must provide or complete.
-
The colored rectangles represent Java Virtual Machines (VMs). All of the
objects inside one Virtual Machine can communicate using ordinary (local)
Java message send. Between VMs, the only way to communicate is to use an
operating system service, such as a network socket.
-
To make your experience more authentic, please try running each VM on a
separate physical machine. However, for debugging purposes, it may be convenient
to run all three on the same physical machine.
-
The CSE515Registry could run on a third machine, or it could run on the
same machine as one of the other VMs.
How it works
Getting Started: lookup and rebind
Initially, CSE515Registry must
be started (say, from the command line). It will wait for either a lookup
request from the client or a
rebind request from the server.
When the server (ZipCodeSeverImpl) initializes
itself and is ready to service requests, it first registers itself in the
CSE515Registry.
It does this using the rebind method of a
RegistryClerk. The server sends a Remote Object Reference (ROR) representing
itself to the clerk, which transmits it to the registry. This ROR contains
the address of the machine on which server its running, the port number
through which clients can communication with the server, the remote interface
name, and a uid identifying the specific server object.
Before the client can make a request to the server, it must first lookup
the server in the registry to obtain an ROR. To do this, the client sends
a lookup message to the RegistryClerk, which
communicates with the registry for the client. In return, it gets the ROR
from the registry. This contains the address of the server's machine and
the port number on which it is listening. It also contains the remote interface
name, which can be used for type checking: ideally, the Client should check
that the interface of the stub is in fact the same as the interface of
the server. (Why might it not be? Because either the client or the server
might be a newer version ... )
Request/Reply
The stub performs the marshaling of the data and sends it over
the socket to the server. On the remote host, YourRMI
is waiting for the request from the client.
Once YourRMI gets the request, it unmarshals
the uid and then gets the skeleton for the
corresponding object. The skeleton unmarshals the message and the arguments,
and invokes the corresponding method on the server (ZipCodeServerImpl).
The server would then process the request and reply to the skeleton, which
would marshal the reply and send a network message back to the stub. Finally,
the stub unmarshals the result and returns to the client.
What is the difference between YourRMI
and skeleton? YourRMI
is library code, and is written once (by you, the RMS implementor). The
skeleton
is generated by the stub generator; it is diferent for each remote interface.
Each stub class has a corresponding skeleton.
Actually, there are some choices to be made about how much work is done
by YourRMI and how much by the skeleton.
YourRMI
could do all of the unmarshaling, and could call the server object directly,
rendering the skeleton unnecessary. In fact, the first implementation of
Java RMI from Sun used a skeleton, whereas the current implementation does
not.
Similarly, on the client side, the stub should do the work that is specific
to the particular interface being implemented (ZipCodeDirectory),
but the stub may want to use some library objects (written by you!) to
take care of the generic work. In other words, the "generated" code in
the stub should be limited to the code that really does change from one
interface to another.
Code Provided:
-
YourRMI.java: an outline of
a server-side Communication Module. You must complete it.
-
RORtbl.java: an outline of the
implementation of the ROR table. You must complete it.
The Zip Code Example
In this assignment, we will be using the example of a server
that maps town names to Zip Codes.
-
A list of towns and their Zip codes is stored on the server.
-
The server can map a town name to a Zip code (method find),
-
The server can provide the entire list of towns and corresponding Zip codes
to the sender (method findAll).
-
The server's list of towns and codes can be initialized (method initialize)
-
For debugging, the server can print the whole list on standard Output (method
printAll).
The code provided for this is as follows.
-
ZipCodeDirectory.java: defines
the above interface.
-
ZipCodeServerImpl.java: the
zip code server implementation.
-
ZipCodeList.java: A utility class
used by the client and the ZipCodeServer
-
ZipCodeClient.java: a client
program.
-
data.txt: Example data - towns and their
Zip Codes.
The Registry, and Remote Object References.
The registry (sometimes called the binder ) is
a separate service that maintains a table of mappings from textual names
to Remote Object references. It is used by servers to register their remote
objects by name, and by clients to look them up. It needs to be running
when you try to initialize client-server communication.
A Remote Object Reference is an object that contains all of the
information necessary to initiate a remote message send: the address of
the machine hosting the remote object, the port number for the server process,
the object id of the object and interface that the object exports.
How is an Remote Objcet Reference different from a Proxy? They contain
essentially the same information (or a reference to the same information).
However, in Java the type system prevents them from being the same. The
Proxy object has an interface that is identical to the object that it represents,
and this will of course be different for each remote interface. Remote
Object Reference instances have a method (localize)
that creates the Proxy class instance.
The registry provides a way (not using RMS!) for the client to get the
first Remote Object Reference (ROR), which is needed to send the first
remote message. (Once the first message has been sent to an object, the
reply can contain additional remote object references.)
The classes provided are:
-
CSE515Registry.java: the registry
program itself. It creates the table and stores the mapping.
-
RegistryThread.java: a utility
class used by the registry program.
-
RegistryClerk.java: the class
that defines the behavior of a RegsitryClerk. To create a clerk, use the
constructor of this class. The clerk provides the lookup
method for the client and the rebind method
for the server.
-
RemoteObjectRef.java: the class
whose instances represent Remote Object References
What you should do:
-
Copy all of the sample programs to your own working directory. All of the
code is here; it is also available as a zip archive, for easier copying.
(Beware! The zipped versions have DOS line ending conventions.)
-
Complete the program YourRMI.java, so that
it starts the ZipCodeServer.
-
Complete the class RORtbl.
-
Generate by Hand the client stubs for the ZipCodeDirectory. You
are not required to write a stub compiler for this assignment. (You
can do so if you wish.) Instead, write the stub object for this particular
interface by hand.
-
If your design uses a server-specific skeleton (also called a server stub),
generate the skeleton by hand too. Alternatively, you may choose not to
have a skeleton, and to perform completely generic server-side unmarshaling
and marshaling using reflection.
The stubs, which are the methods on the Proxy, marshall and send messages
to the server module. The design to be followed for marshaling and unmarshaling
can be one of the following:
-
Use of Reflection;
-
Use Java Serialization (this method is simpler).
Refer to section
4.3.2 of Coulouris for more information. In both cases, make a stream
out of the data and send it via a TCP socket.
-
Link your RMS system with the ZipCodeClient and Server provided, and test
them.
-
Think about the following issue. When marshlling a reply from the server
to the client, what should you do with objects in result? Should you marshal
copies, or should you send back RORs to the original objects, which remain
on the server. Write a paragraph summarizing your conclusions.
-
For extra credit, if you so desire (but not required!)
-
Instead of using TCP, use UDP (DatagramSocket) and implement the Birrell
and Nelson packet exchange protocol.
-
Time your implementation of remote messages in the same way as you did
in assignment 2. This will require implementing another client and server.
-
Write a stub compiler. The input should be the interface of the service,
e.g.,
ZipCodeDirectory.java
Executing the Programs
-
On machine one.somedomain.net, run the registry
program CSE515Registry:
java CSE515Registry <registry port number>
Note: Port numbers are integers in the range 0 to 65535;
however, low numbered ports are reserved for system servers, and require
system privileges.
-
On your server machine:
java YourRMI ZipCodeServerImpl one.somedomain.net
<registryPortNumber> ZipCodeServer
The YourRMI program takes as
arguments:
-
the name of the class that implements the service (ZipCodeServerImpl);
-
the IP address and port at which the registry can be found; and
-
the name of the service that will be advertised to the registry, a string
(ZipCodeServer).
-
On your client machine
Java ZipCodeClient one.somedomain.net <registryPortNumber>
ZipCodeServer data.txt
Useful Resources
-
For socket programming , refer to classes java.net.Socket and java.net.ServerSocket
in the Java API.
-
Refer to java.lang.Object and java.lang.Class of the Java API for the methods
getClass(),
newInstance(),
etc,
-
For serialization, refer to the interface java.io.Serializable in the Java
API. The Serialization Specification may be useful too.
-
For reflection, see the package java.lang.reflect.
-
Useful notes from Coulouris
et al section 4.3.2
on how to serialize data in Java.
A Final Caution
The code samples have passed through many hands. There may
be bugs in them! If something looks bogus, ask the TA about it before
you spend hours puzzling over it.
|