Project 5 - Robust Peer-to-Peer (P2P) messaging system

Due April 25, 6:00PM

We start with some of the same basic concepts as in Project 4, but make the system more robust and less dependent on a central directory. Overall, the amount of code you need to write is about twice the amount of code for Project 4.

Messages

The class Message is now an abstract class, with a GUID field (containing a RemoteClient and a sequence number) and a set of RemoteClients that have seen the Message. The class Message implements Runnable, so any message can have a arbitrary payload.

When a message arrives, check to see if a message with an identical GUID has arrived recently (e.g., 1-5 minutes). If so, ignore the message.

Otherwise,

For each particular remote client, you should limit the number of outstanding messages you send to that client, in case the client is slow or deadlocked. There are several ways of implementing this.

If in initiating or forwarding a message to a client, you get an error, you should remove that client from your list of known live clients.

We are providing two example classes derived from Message, PingMessage and TextMessagePingMessage has an empty run() method, so just tells other clients that the initiating client is live, and TextMessage prints a String in its runTextMessage() method, as in Project 4.

Discovery

One way to learn about other clients is to receive messages. But if you don't know about any other clients, you can't bootstrap the process.

Every client must create an RMIRegistery on port x33yy, where x is your section number and yy is your account number. Your client must register itself with that registry, whatever name you wish (e.g., "BillsClient").

The arguments passed to LocalClient.initialize() are a set of strings, each of which should be looked up using Naming.lookup() and added to the list of live known clients.  Those arguments are passed as URL strings on the command line. When testing your client, you should provide rmi://savoir.cs.umd.edu/wp43301 as a client to connect to.

Create a Discovery object using the string appropriate for looking up yourself in your RMIregistry. Calling Discovery.broadcast() will broadcast your RMI URL to every client on the LAN. Calling Discovery.receive() on a discovery object will block until it sees a broadcast on the LAN and will return the string that was broadcast.

You should call Discovery.broadcast() once every 5 minutes.

You should have a try block that just calls Discovery.receive(). For each string received, use Naming.lookup() to look it up and add the client to your list of known live clients.

Robustness

You should catch exceptions to deal with message errors.  In particular, you must catch RemoteExceptions to deal with a remote client failing while your client is sending it a message.

Your client must be able to cope with slow, deadlocked, or otherwise uncooperative clients when you send a message.  That will require using threads in some reasonable way for message sends, and then limiting the number of outstanding sends to any one client to a small number.  The idea is that your client should not hang because of a small number of other clients behaving badly.  Of course, in those situations some clients may not see all messages, but that's OK.  Also, you should be sure to properly synchronize all operations on objects that are shared by multiple threads.

You need to allow code for stubs and Messages from other clients to be downloaded into your JVM, and you also need to allow your stub and any message classes you define to be downloaded to other clients. Security is an important issue in this. You should not allow the downloaded code arbitrary access to all resources where your client is running. You should allow network access to unprivileged ports (i.e. port 1024+), but definitely not any kind of access to local files.

Documentation and implementation

The Java documentation for the interfaces and classes you will implement are here.  A jar file containing the Java code for the interfaces and classes we provide is here, and is also available on the cluster in Dr. Pugh's and Dr. Sussman's accounts.

Note that all the provided interfaces and classes are in a set of packages rooted at cmsc433.p5.  Your client implementation should be placed in a package named cmsc433.userid.p5 , with userid replaced with your class account ID.

We also provide three sample client driver classes, Counting, Input and Time, that initiate TextMessages.