CSC 125/259 : Horse Race Part II In part 1 of the assignment, a central server had to coordinate each step of the race. In the second part, there will still be a central server but this server will now only be responsible for coordinating the start of the race. Once the race has started, the "horses" will coordinate themselves by passing around a "token". This will remove most of the central-server bottleneck. This assignment will require you to modify both hrserver and hrclient (create different copies of these programs first). You can use the following interfaces and simple classes in your program (place them in different files) import java.rmi.*; public interface hrcoordinator extends Remote // new RMI interface for server { startinfo joinrace(String name, String ip) throws RemoteException; } The only central-server function will be joinrace. Note that when called, you will be passing not just a horse name but also an ip addr of where the horse is. To simplify things we will just use strings to represent ips (or hostnames). The central server returns the following structure: public class startinfo implements java.io.Serializable { public byte id; // id of racer assigned by the server; 0 will be initiator public horse[] horses; public String[] addrs; // string vector that parallels the horses vector // each vector should contain the ip addr of the corresponding // horse - other info such as horse name are already in horse // structure public startinfo(byte i, horse[] h) {id=i; horses=h;} public void setaddr(int i, String ip) { addrs[i] = ip; } } This is essentially the same as "raceinfo" in the original program except that it also includes the ips of each horse, so each horse will know who its neighbor is. The byte id in this structure is critical: it identifies your position in the array. This id is assigned by the server, presumably depending on who called joinrace first. The horse that's assigned id 0 will be responsible for starting the race by creating a token and start passing it around. If there are HN horses in the race then horse i will pass the token to horse (i+1)%HN, thus creating a "token ring". Each horse-agent now must also implement the following RMI remote interface: import java.rmi.*; public interface tokenagent extends Remote { void recvtoken(tokeninfo tif) throws RemoteException; // called by neighbor in ring } The tokeninfo structure that's passed here is the following: public class tokeninfo implements java.io.Serializable { public byte senderid; // sender of token public int[] Xpos; // x-position of each horse, -1 can indicate horse not in race public tokeninfo(byte a, int[] x) {senderid=a; Xpos=x;} } The idea is that each time horse i holds the token, it can increment Xpos[i] by a random number between 1 and 6 (inclusive - call randint). It then passes the tokeninfo to the next horse in the ring. HOWEVER, it will not actually update its display (call nextframe) or calculate who's the leader (call determineLeader) until it has received the token BACK. This way each horse will get a chance to increment its position during each round. (there's a slight flaw in this algorithm - don't worry about it.) Hint: recvtoken should be synchronized, so you don't want to call your neighbor's recvtoken function inside this function! Register the reception and get out! Here's some code and outline that you can use to modify the init() function in hrclient. // Register your tokenagent server with local rmiregistry - use the // name "tokenagentx" where x is the your id number. System.out.println("waiting for other racers ..."); startinfo idh = null; try { idh = server.joinrace(args[0], args[1]); } catch (RemoteException re) {System.out.println(re); System.exit(1);} id = idh.id; // id returned by server; H = idh.horses; HN = H.length; // when returned, this will be # of horses in race String ips = idh.addrs; // store ip addrs for future reference, probably // want to assign to something other than a local var though! // now you need to call Naming.lookup on your right neighbor's registry. // if you are id i you will be looking up the string "tokenagent"+((i+1)%HN). // if you have been assigned id 0 by the central server, you need to create // the initial token structure, with each x position initially 0, and call // recvtoken on your (right-hand) neighbor - this part doesn't have to be // done in init() // do not declare a winner just because you've crossed the finish line: // stop moving forward, but wait for the token to come around one more time // before stopping. ================== Your horse race client must work with everyone else's! ==== A few of your classmates may try to cheat by decrementing your x position, ==== so you want to check if the in the tokeninfo that you got back is what it was ==== supposed to be. ====================== FAULT TOLERANCE: ========================================= Fault tolerence becomes harder in this scenario: if a horse goes down, it will be detected by its predecessor in the ring. Since this agent holds the token, it can choose to send the token to the next agent in the ring. Of course the danger here is that the horse "isn't really dead" but might be temporarily lost due to, for example, network congestion. In that case there is the danger of this horse sending a second token. That's what the senderid is for. One solution to this problem is to kick the slow horse out of the race by setting its x position in the ring to -1, then ignore any tokeninfo sent by this agent. Making fault-tolerence completely safe is difficult to guarantee, however. What if the horse-agent holding the token goes down? This part is do-your best. When the configuration of the distributed program changes, all the agents need to first agree what the new configuration is. In this case, each agent needs to know who its neighbor is, and who should be the process that sends out the initial token (which remaining agent has smallest id, in case agent 0 went down). One solution is to have each agent pass around the ring a structure concerning what it knows as the current configuration, a structure similar to startinfo. When an agent receives this strucutre and notices some false or missing information, it updates it and passes it onwards. When the structure comes all the way back to you around the ring and has not been altered, then you know that the ring has stablized. The agent with the smalles id can then retransmit the token so that the application can continue running.