Java/Network Protocol/RMI

Материал из Java эксперт
Перейти к: навигация, поиск

RMI based Bank system

   <source lang="java">

/*

* Copyright (c) 2004 David Flanagan.  All rights reserved.
* This code is from the book Java Examples in a Nutshell, 3nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose,
* including teaching and use in open-source projects.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book, 
* please visit http://www.davidflanagan.ru/javaexamples3.
*/

package je3.rmi; import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; import java.io.*; import java.util.*; import je3.rmi.Mud.*; /**

* This class is a client program for the MUD.  The main() method sets up 
* a connection to a RemoteMudServer, gets the initial RemoteMudPlace object,
* and creates a MudPerson object to represent the user in the MUD.  Then it 
* calls runMud() to put the person in the place, begins processing
* user commands.  The getLine() and getMultiLine() methods are convenience
* methods used throughout to get input from the user.
**/

public class MudClient {

   /**
    * The main program.  It expects two or three arguments:
    *   0) the name of the host on which the mud server is running
    *   1) the name of the MUD on that host
    *   2) the name of a place within that MUD to start at (optional).
    *
    * It uses the Naming.lookup() method to obtain a RemoteMudServer object
    * for the named MUD on the specified host.  Then it uses the getEntrance()
    * or getNamedPlace() method of RemoteMudServer to obtain the starting
    * RemoteMudPlace object.  It prompts the user for a their name and 
    * description, and creates a MudPerson object.  Finally, it passes
    * the person and the place to runMud() to begin interaction with the MUD.
    **/
   public static void main(String[] args) {
       try {
           String hostname = args[0]; // Each MUD is uniquely identified by a 
           String mudname = args[1];  //   host and a MUD name.
           String placename = null;   // Each place in a MUD has a unique name
           if (args.length > 2) placename = args[2];
     
           // Look up the RemoteMudServer object for the named MUD using
           // the default registry on the specified host.  Note the use of
           // the Mud.mudPrefix constant to help prevent naming conflicts
           // in the registry.
           RemoteMudServer server = 
               (RemoteMudServer)Naming.lookup("rmi://" + hostname + "/" +
                Mud.mudPrefix + mudname);
           // If the user did not specify a place in the mud, use
           // getEntrance() to get the initial place.  Otherwise, call
           // getNamedPlace() to find the initial place.
           RemoteMudPlace location = null;
           if (placename == null) location = server.getEntrance();
           else location = (RemoteMudPlace) server.getNamedPlace(placename);
     
           // Greet the user and ask for their name and description.
           // This relies on getLine() and getMultiLine() defined below.
           System.out.println("Welcome to " + mudname);
           String name = getLine("Enter your name: ");
           String description = getMultiLine("Please describe what " +
           "people see when they look at you:");
           // Define an output stream that the MudPerson object will use to
           // display messages sent to it to the user.  We"ll use the console.
           PrintWriter myout = new PrintWriter(System.out);
     
           // Create a MudPerson object to represent the user in the MUD.
           // Use the specified name and description, and the output stream.
           MudPerson me = new MudPerson(name, description, myout);
     
           // Lower this thread"s priority one notch so that broadcast
           // messages can appear even when we"re blocking for I/O.  This is
           // necessary on the Linux platform, but may not be necessary on all
           // platforms.
           int pri = Thread.currentThread().getPriority();
           Thread.currentThread().setPriority(pri-1);
     
           // Finally, put the MudPerson into the RemoteMudPlace, and start
           // prompting the user for commands.
           runMud(location, me);
       }
       // If anything goes wrong, print a message and exit.
       catch (Exception e) {
           System.out.println(e);
           System.out.println("Usage: java MudClient <host> <mud> [<place>]");
           System.exit(1);
       }
   }
   /**
    * This method is the main loop of the MudClient.  It places the person
    * into the place (using the enter() method of RemoteMudPlace).  Then it
    * calls the look() method to describe the place to the user, and enters a
    * command loop to prompt the user for a command and process the command
    **/
   public static void runMud(RemoteMudPlace entrance, MudPerson me) 
 throws RemoteException
   {
       RemoteMudPlace location = entrance;  // The current place
       String myname = me.getName();        // The person"s name
       String placename = null;             // The name of the current place
       String mudname = null;             // The name of the mud of that place
       try { 
           // Enter the MUD
           location.enter(me, myname, myname + " has entered the MUD."); 
           // Figure out where we are (for the prompt)
           mudname = location.getServer().getMudName();
           placename = location.getPlaceName();
           // Describe the place to the user
           look(location);
       }
       catch (Exception e) {
           System.out.println(e);
           System.exit(1);
       }
 
       // Now that we"ve entered the MUD, begin a command loop to process
       // the user"s commands.  Note that there is a huge block of catch
       // statements at the bottom of the loop to handle all the things that
       // could go wrong each time through the loop.
       for(;;) {  // Loop until the user types "quit"
           try {    // Catch any exceptions that occur in the loop
               // Pause just a bit before printing the prompt, to give output
               // generated indirectly by the last command a chance to appear.
               try { Thread.sleep(200); } catch (InterruptedException e) {}
               // Display a prompt, and get the user"s input
               String line = getLine(mudname + "." + placename + "> ");
   
               // Break the input into a command and an argument that consists
               // of the rest of the line.  Convert the command to lowercase.
               String cmd, arg;
               int i = line.indexOf(" ");
               if (i == -1) { cmd = line; arg = null; }
               else {
                   cmd = line.substring(0, i).toLowerCase();
                   arg = line.substring(i+1);
               }
               if (arg == null) arg = "";
   
               // Now go process the command.  What follows is a huge repeated
               // if/else statement covering each of the commands supported by
               // this client.  Many of these commands simply invoke one of
               // the remote methods of the current RemoteMudPlace object.
               // Some have to do a bit of additional processing.
               // LOOK: Describe the place and its things, people, and exits
               if (cmd.equals("look")) look(location);
               // EXAMINE: Describe a named thing
               else if (cmd.equals("examine")) 
                   System.out.println(location.examineThing(arg));
               // DESCRIBE: Describe a named person
               else if (cmd.equals("describe")) {
                   try { 
                       RemoteMudPerson p = location.getPerson(arg);
                       System.out.println(p.getDescription()); 
                   }
                   catch(RemoteException e) {
                       System.out.println(arg + " is having technical " +
            "difficulties. No description " +
            "is available.");
                   }
               }
               // GO: Go in a named direction
               else if (cmd.equals("go")) {
                   location = location.go(me, arg);
                   mudname = location.getServer().getMudName();
                   placename = location.getPlaceName();
                   look(location);
               }
               // SAY: Say something to everyone 
               else if (cmd.equals("say")) location.speak(me, arg);
               // DO: Do something that will be described to everyone
               else if (cmd.equals("do")) location.act(me, arg);
               // TALK: Say something to one named person
               else if (cmd.equals("talk")) {
                   try {
                       RemoteMudPerson p = location.getPerson(arg);
                       String msg = getLine("What do you want to say?: ");
                       p.tell(myname + " says \"" + msg + "\"");
                   }
                   catch (RemoteException e) {
                       System.out.println(arg + " is having technical " +
                   "difficulties. Can"t talk to them.");
                   }
               }
               // CHANGE: Change my own description 
               else if (cmd.equals("change"))
                   me.setDescription(
         getMultiLine("Describe yourself for others: "));
               // CREATE: Create a new thing in this place
               else if (cmd.equals("create")) {
                   if (arg.length() == 0)
                       throw new IllegalArgumentException("name expected");
                   String desc = getMultiLine("Please describe the " +
                arg + ": ");
                   location.createThing(me, arg, desc);
               }
               // DESTROY: Destroy a named thing
               else if (cmd.equals("destroy")) location.destroyThing(me, arg);
               // OPEN: Create a new place and connect this place to it
               // through the exit specified in the argument.
               else if (cmd.equals("open")) {
                   if (arg.length() == 0) 
                     throw new IllegalArgumentException("direction expected");
                   String name = getLine("What is the name of place there?: ");
                   String back = getLine("What is the direction from " + 
           "there back to here?: ");
                   String desc = getMultiLine("Please describe " +
                name + ":");
                   location.createPlace(me, arg, back, name, desc);
               }
               // CLOSE: Close a named exit.  Note: only closes an exit
               // uni-directionally, and does not destroy a place.
               else if (cmd.equals("close")) {
                   if (arg.length() == 0) 
                     throw new IllegalArgumentException("direction expected");
                   location.close(me, arg);
               }
               // LINK: Create a new exit that connects to an existing place
               // that may be in another MUD running on another host
               else if (cmd.equals("link")) {
                   if (arg.length() == 0) 
                     throw new IllegalArgumentException("direction expected");
                   String host = getLine("What host are you linking to?: ");
                   String mud =
     getLine("What is the name of the MUD on that host?: ");
                   String place =
     getLine("What is the place name in that MUD?: ");
                   location.linkTo(me, arg, host, mud, place);
                   System.out.println("Don"t forget to make a link from " +
              "there back to here!");
               }
               // DUMP: Save the state of this MUD into the named file,
               // if the password is correct
               else if (cmd.equals("dump")) {
                   if (arg.length() == 0) 
                      throw new IllegalArgumentException("filename expected");
                   String password = getLine("Password: ");
                   location.getServer().dump(password, arg);
               }
               // QUIT: Quit the game
               else if (cmd.equals("quit")) {
                   try { location.exit(me, myname + " has quit."); } 
                   catch (Exception e) {}
                   System.out.println("Bye.");
                   System.out.flush();
                   System.exit(0);
               }
               // HELP: Print out a big help message
               else if (cmd.equals("help")) System.out.println(help);
               // Otherwise, this is an unrecognized command.
               else System.out.println("Unknown command.  Try "help".");
           }
           // Handle the many possible types of MudException
           catch (MudException e) {
               if (e instanceof NoSuchThing) 
                   System.out.println("There isn"t any such thing here."); 
               else if (e instanceof NoSuchPerson) 
                  System.out.println("There isn"t anyone by that name here.");
               else if (e instanceof NoSuchExit) 
                 System.out.println("There isn"t an exit in that direction.");
               else if (e instanceof NoSuchPlace) 
                   System.out.println("There isn"t any such place."); 
               else if (e instanceof ExitAlreadyExists)
                   System.out.println("There is already an exit " +
              "in that direction.");
               else if (e instanceof PlaceAlreadyExists)
                   System.out.println("There is already a place " +
              "with that name.");
               else if (e instanceof LinkFailed)
                   System.out.println("That exit is not functioning.");
               else if (e instanceof BadPassword) 
                   System.out.println("Invalid password."); 
               else if (e instanceof NotThere)      // Shouldn"t happen
                   System.out.println("You can"t do that when " +
              "you"re not there."); 
               else if (e instanceof AlreadyThere)  // Shouldn"t happen
                   System.out.println("You can"t go there; " +
              "you"re already there.");
           }
           // Handle RMI exceptions
           catch (RemoteException e) {
              System.out.println("The MUD is having technical difficulties.");
              System.out.println("Perhaps the server has crashed:");
              System.out.println(e);
           }
           // Handle everything else that could go wrong.
           catch (Exception e) {
               System.out.println("Syntax or other error:");
               System.out.println(e);
               System.out.println("Try using the "help" command.");
           }
       }
   }
   
   /** 
    * This convenience method is used in several places in the
    * runMud() method above.  It displays the name and description of
    * the current place (including the name of the mud the place is in), 
    * and also displays the list of things, people, and exits in
    * the current place.
    **/
   public static void look(RemoteMudPlace p) 
 throws RemoteException, MudException
   {
       String mudname = p.getServer().getMudName(); // Mud name
       String placename = p.getPlaceName();         // Place name
       String description = p.getDescription();     // Place description
       Vector things = p.getThings();               // List of things here
       Vector names = p.getNames();                 // List of people here
       Vector exits = p.getExits();                 // List of exits from here
       // Print it all out
       System.out.println("You are in: " + placename +
        " of the Mud: " + mudname);
       System.out.println(description);
       System.out.print("Things here: ");
       for(int i = 0; i < things.size(); i++) {      // Display list of things
           if (i > 0) System.out.print(", ");
           System.out.print(things.elementAt(i));
       }
       System.out.print("\nPeople here: ");
       for(int i = 0; i < names.size(); i++) {       // Display list of people
           if (i > 0) System.out.print(", ");
           System.out.print(names.elementAt(i));
       }
       System.out.print("\nExits are: ");
       for(int i = 0; i < exits.size(); i++) {       // Display list of exits
           if (i > 0) System.out.print(", ");
           System.out.print(exits.elementAt(i));
       }
       System.out.println();                         // Blank line
       System.out.flush();                           // Make it appear now!
   }
   
   /** This static input stream reads lines from the console */
   static BufferedReader in =
 new BufferedReader(new InputStreamReader(System.in));
   
   /** 
    * A convenience method for prompting the user and getting a line of 
    * input.  It guarantees that the line is not empty and strips off 
    * whitespace at the beginning and end of the line.
    **/
   public static String getLine(String prompt) {
       String line = null;
       do {                      // Loop until a non-empty line is entered
           try {
               System.out.print(prompt);             // Display prompt
               System.out.flush();                   // Display it right away
               line = in.readLine();                 // Get a line of input
               if (line != null) line = line.trim(); // Strip off whitespace
           } catch (Exception e) {}                // Ignore any errors
       } while((line == null) || (line.length() == 0));
       return line;
   }
   
   /**
    * A convenience method for getting multi-line input from the user.
    * It prompts for the input, displays instructions, and guarantees that
    * the input is not empty.  It also allows the user to enter the name of
    * a file from which text will be read.
    **/
   public static String getMultiLine(String prompt) {
       String text = "";
       for(;;) {  // We"ll break out of this loop when we get non-empty input
           try {
               BufferedReader br = in;       // The stream to read from 
               System.out.println(prompt);   // Display the prompt
               // Display some instructions
               System.out.println("You can enter multiple lines.  " + 
          "End with a "." on a line by itself.\n" +
          "Or enter a "<<" followed by a filename");
               // Make the prompt and instructions appear now.
               System.out.flush();
               // Read lines
               String line;
               while((line = br.readLine()) != null) {    // Until EOF
                   if (line.equals(".")) break;  // Or until a dot by itself
                   // Or, if a file is specified, start reading from it 
                   // instead of from the console.
                   if (line.trim().startsWith("<<")) {      
                       String filename = line.trim().substring(2).trim();
                       br = new BufferedReader(new FileReader(filename));
                       continue;  // Don"t count the << as part of the input
                   }
       // Add the line to the collected input
                   else text += line + "\n";  
               }
               // If we got at least one line, return it.  Otherwise, chastise
               // the user and go back to the prompt and the instructions.
               if (text.length() > 0) return text;
               else System.out.println("Please enter at least one line.");
           }
           // If there were errors, for example an IO error reading a file,
           // display the error and loop again, displaying prompt and
           // instructions
           catch(Exception e) { System.out.println(e); }
       }
   }
   /** This is the usage string that explains the available commands */
   static final String help = 
 "Commands are:\n" + 
 "look: Look around\n" +
 "examine <thing>: examine the named thing in more detail\n" +
 "describe <person>: describe the named person\n" +
 "go <direction>: go in the named direction (i.e. a named exit)\n" +
 "say <message>: say something to everyone\n" +
 "do <message>: tell everyone that you are doing something\n" +
 "talk <person>: talk to one person.  Will prompt for message\n" +
 "change: change how you are described.  Will prompt for input\n" +
 "create <thing>: create a new thing.  Prompts for description \n" +
 "destroy <thing>: destroy a thing.\n" + 
 "open <direction>: create an adjoining place. Prompts for input\n"+
 "close <direction>: close an exit from this place.\n" +
 "link <direction>: create an exit to an existing place,\n" +
 "     perhaps on another server.  Will prompt for input.\n" +
 "dump <filename>: save server state.  Prompts for password\n" +
 "quit: leave the Mud\n" +
 "help: display this message";

} /*

* Copyright (c) 2004 David Flanagan.  All rights reserved.
* This code is from the book Java Examples in a Nutshell, 3nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose,
* including teaching and use in open-source projects.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book, 
* please visit http://www.davidflanagan.ru/javaexamples3.
*/

package je3.rmi; import java.rmi.*; import java.rmi.server.*; import java.io.*; import je3.rmi.Mud.*; /**

* This is the simplest of the remote objects that we implement for the MUD.
* It maintains only a little bit of state, and has only two exported
* methods 
**/

public class MudPerson extends UnicastRemoteObject implements RemoteMudPerson {

   String name;             // The name of the person 
   String description;      // The person"s description
   PrintWriter tellStream;  // Where to send messages we receive to
   
   public MudPerson(String n, String d, PrintWriter out)
 throws RemoteException
   {
       name = n;
       description = d;
       tellStream = out;
   }
   
   /** Return the person"s name.  Not a remote method */
   public String getName() { return name; }
   
   /** Set the person"s name.  Not a remote method */
   public void setName(String n) { name = n; }
   
   /** Set the person"s description.  Not a remote method */
   public void setDescription(String d) { description = d; }
   
   /** Set the stream that messages to us should be written to. Not remote. */
   public void setTellStream(PrintWriter out) { tellStream = out; }
   
   /** A remote method that returns this person"s description */
   public String getDescription() throws RemoteException {
 return description;
   }
   
   /** 
    * A remote method that delivers a message to the person.
    * I.e. it delivers a message to the user controlling the "person"
    **/
   public void tell(String message) throws RemoteException {
       tellStream.println(message);
       tellStream.flush();
   }

} /*

* Copyright (c) 2004 David Flanagan.  All rights reserved.
* This code is from the book Java Examples in a Nutshell, 3nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose,
* including teaching and use in open-source projects.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book, 
* please visit http://www.davidflanagan.ru/javaexamples3.
*/

package je3.rmi; import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; import java.io.*; import java.util.*; import je3.rmi.Mud.*; /**

* This class implements the RemoteMudPlace interface and exports a
* bunch of remote methods that are at the heart of the MUD.  The
* MudClient interacts primarily with these methods.  See the comment
* for RemoteMudPlace for an overview.
* The MudPlace class is Serializable so that places can be saved to disk
* along with the MudServer that contains them.  Note, however that the
* names and people fields are marked transient, so they are not serialized
* along with the place (because it wouldn"t make sense to try to save
* RemoteMudPerson objects, even if they could be serialized).
**/

public class MudPlace extends UnicastRemoteObject

   implements RemoteMudPlace, Serializable

{

   String placename, description;          // information about the place
   Vector exits = new Vector();            // names of exits from this place
   Vector destinations = new Vector();     // where the exits go to
   Vector things = new Vector();           // names of things in this place
   Vector descriptions = new Vector();     // descriptions of those things
   transient Vector names = new Vector();  // names of people in this place
   transient Vector people = new Vector(); // the RemoteMudPerson objects
   MudServer server;                       // the server for this place
   
   /** A no-arg constructor for de-serialization only.  Do not call it */
   public MudPlace() throws RemoteException { super(); }
   
   /**
    * This constructor creates a place, and calls a server method
    * to register the object so that it will be accessible by name
    **/
   public MudPlace(MudServer server, String placename, String description) 
 throws RemoteException, PlaceAlreadyExists
   {
       this.server = server;
       this.placename = placename; 
       this.description = description;
       server.setPlaceName(this, placename);  // Register the place
   }
   
   /** This remote method returns the name of this place */
   public String getPlaceName() throws RemoteException { return placename; }
   
   /** This remote method returns the description of this place */
   public String getDescription() throws RemoteException {
 return description;
   }
   /** This remote method returns a Vector of names of people in this place */
   public Vector getNames() throws RemoteException { return names; }
   
   /** This remote method returns a Vector of names of things in this place */
   public Vector getThings() throws RemoteException { return things; }
   
   /** This remote method returns a Vector of names of exits from this place*/
   public Vector getExits() throws RemoteException { return exits; }
   /** 
    * This remote method returns a RemoteMudPerson object corresponding to
    * the specified name, or throws an exception if no such person is here 
    **/
   public RemoteMudPerson getPerson(String name) 
 throws RemoteException, NoSuchPerson
   {
       synchronized(names) {
           // What about when there are 2 of the same name?
           int i = names.indexOf(name);
           if (i == -1) throw new NoSuchPerson();
           return (RemoteMudPerson) people.elementAt(i);
       }
   }
   
   /** 
    * This remote method returns a description of the named thing, or
    * throws an exception if no such thing is in this place.
    **/
   public String examineThing(String name) throws RemoteException, NoSuchThing
   {
       synchronized(things) {
           int i = things.indexOf(name);
           if (i == -1) throw new NoSuchThing();
           return (String) descriptions.elementAt(i);
       }
   }
   
   /** 
    * This remote method moves the specified RemoteMudPerson from this place
    * in the named direction (i.e. through the named exit) to whatever place
    * is there.  It throws exceptions if the specified person isn"t in this
    * place to begin with, or if they are already in the place through the 
    * exit or if the exit doesn"t exist, or if the exit links to another MUD 
    * server and the server is not functioning.
    **/
   public RemoteMudPlace go(RemoteMudPerson who, String direction) 
 throws RemoteException, NotThere, AlreadyThere, NoSuchExit, LinkFailed
   {
       // Make sure the direction is valid, and get destination if it is
       Object destination;
       synchronized(exits) {
           int i = exits.indexOf(direction);
           if (i == -1) throw new NoSuchExit();
           destination = destinations.elementAt(i);
       }
 
       // If destination is a string, it is a place on another server, so
       // connect to that server.  Otherwise, it is a place already on this
       // server.  Throw an exception if we can"t connect to the server.
       RemoteMudPlace newplace;
       if (destination instanceof String) {
           try { 
               String t = (String) destination;
               int pos = t.indexOf("@");
               String url = t.substring(0, pos);
               String placename = t.substring(pos+1);
               RemoteMudServer s = (RemoteMudServer) Naming.lookup(url);
               newplace = s.getNamedPlace(placename);
           } 
           catch (Exception e) { throw new LinkFailed(); } 
       }
       // If the destination is not a string, then it is a Place
       else newplace = (RemoteMudPlace) destination;
 
       // Make sure the person is here and get their name.  
       // Throw an exception if they are not here
       String name = verifyPresence(who);
 
       // Move the person out of here, and tell everyone who remains about it.
       this.exit(who, name + " has gone " + direction);
 
       // Put the person into the new place.  
       // Send a message to everyone already in that new place
       String fromwhere;
       if (newplace instanceof MudPlace) // going to a local place
           fromwhere = placename;
       else
           fromwhere = server.getMudName() + "." + placename;
       newplace.enter(who, name, name + " has arrived from: " + fromwhere);
 
       // Return the new RemoteMudPlace object to the client so they
       // know where they are now at.
       return newplace;
   }
   
   /** 
    * This remote method sends a message to everyone in the room.  Used to
    * say things to everyone.  Requires that the speaker be in this place.
    **/
   public void speak(RemoteMudPerson speaker, String msg) 
 throws RemoteException, NotThere
   {
       String name = verifyPresence(speaker);
       tellEveryone(name + ":" + msg);
   }
   
   /** 
    * This remote method sends a message to everyone in the room.  Used to
    * do things that people can see. Requires that the actor be in this place.
    **/
   public void act(RemoteMudPerson actor, String msg)
 throws RemoteException, NotThere
   {
       String name = verifyPresence(actor);
       tellEveryone(name + " " + msg);
   }
   /** 
    * This remote method creates a new thing in this room.
    * It requires that the creator be in this room.
    **/
   public void createThing(RemoteMudPerson creator,
         String name, String description) 
 throws RemoteException, NotThere, AlreadyThere
   {
       // Make sure the creator is here
       String creatorname = verifyPresence(creator);
       synchronized(things) {
           // Make sure there isn"t already something with this name.  
           if (things.indexOf(name) != -1) throw new AlreadyThere();
           // Add the thing name and descriptions to the appropriate lists
           things.addElement(name);
           descriptions.addElement(description);
       }
       // Tell everyone about the new thing and its creator
       tellEveryone(creatorname + " has created a " + name);
   }
   
   /**
    * Remove a thing from this room.  Throws exceptions if the person
    * who removes it isn"t themselves in the room, or if there is no
    * such thing here.
    **/
   public void destroyThing(RemoteMudPerson destroyer, String thing) 
 throws RemoteException, NotThere, NoSuchThing
   {
       // Verify that the destroyer is here
       String name = verifyPresence(destroyer);
       synchronized(things) {
           // Verify that there is a thing by that name in this room
           int i = things.indexOf(thing);
           if (i == -1) throw new NoSuchThing();
           // And remove its name and description from the lists
           things.removeElementAt(i);
           descriptions.removeElementAt(i);
       }
       // Let everyone know of the demise of this thing.
       tellEveryone(name + " had destroyed the " + thing);
   }
   /**
    * Create a new place in this MUD, with the specified name an description. 
    * The new place is accessible from this place through
    * the specified exit, and this place is accessible from the new place 
    * through the specified entrance.  The creator must be in this place
    * in order to create a exit from this place.
    **/
   public void createPlace(RemoteMudPerson creator,
         String exit, String entrance, String name, 
         String description) 
 throws RemoteException,NotThere,ExitAlreadyExists,PlaceAlreadyExists
   {
       // Verify that the creator is actually here in this place
       String creatorname = verifyPresence(creator);
       synchronized(exits) {  // Only one client may change exits at a time
           // Check that the exit doesn"t already exist.
           if (exits.indexOf(exit) != -1) throw new ExitAlreadyExists();
           // Create the new place, registering its name with the server
           MudPlace destination = new MudPlace(server, name, description);
           // Link from there back to here
           destination.exits.addElement(entrance);
           destination.destinations.addElement(this);
           // And link from here to there
           exits.addElement(exit);
           destinations.addElement(destination);
       }
       // Let everyone know about the new exit, and the new place beyond
       tellEveryone(creatorname + " has created a new place: " + exit);
   }
   
   /**
    * Create a new exit from this mud, linked to a named place in a named
    * MUD on a named host (this can also be used to link to a named place in 
    * the current MUD, of course).  Because of the possibilities of deadlock,
    * this method only links from here to there; it does not create a return
    * exit from there to here.  That must be done with a separate call.
    **/
   public void linkTo(RemoteMudPerson linker, String exit, 
          String hostname, String mudname, String placename) 
 throws RemoteException, NotThere, ExitAlreadyExists, NoSuchPlace
   {
       // Verify that the linker is actually here 
       String name = verifyPresence(linker);
 
       // Check that the link target actually exists.  Throw NoSuchPlace if
       // not.  Note that NoSuchPlace may also mean "NoSuchMud" or
       // "MudNotResponding".
       String url = "rmi://" + hostname + "/" + Mud.mudPrefix + mudname;
       try {
           RemoteMudServer s = (RemoteMudServer) Naming.lookup(url);
           RemoteMudPlace destination = s.getNamedPlace(placename);
       }
       catch (Exception e) { throw new NoSuchPlace(); }
       
       synchronized(exits) {
           // Check that the exit doesn"t already exist.
           if (exits.indexOf(exit) != -1) throw new ExitAlreadyExists();
           // Add the exit, to the list of exit names
           exits.addElement(exit);
           // And add the destination to the list of destinations.  Note that
           // the destination is stored as a string rather than as a
           // RemoteMudPlace.  This is because if the remote server goes down
           // then comes back up again, a RemoteMudPlace is not valid, but the
           // string still is.
           destinations.addElement(url + "@" + placename);
       }
       // Let everyone know about the new exit and where it leads
       tellEveryone(name + " has linked " + exit + " to " + 
        """ + placename + "" in MUD "" + mudname + 
        "" on host " + hostname);
   }
   
   /**
    * Close an exit that leads out of this place.
    * It does not close the return exit from there back to here.
    * Note that this method does not destroy the place that the exit leads to.
    * In the current implementation, there is no way to destroy a place.
    **/
   public void close(RemoteMudPerson who, String exit) 
 throws RemoteException, NotThere, NoSuchExit
   {
       // check that the person closing the exit is actually here
       String name = verifyPresence(who);
       synchronized(exits) {
           // Check that the exit exists
           int i = exits.indexOf(exit);
           if (i == -1) throw new NoSuchExit();
           // Remove it and its destination from the lists
           exits.removeElementAt(i);
           destinations.removeElementAt(i);
       }
       // Let everyone know that the exit doesn"t exist anymore
       tellEveryone(name + " has closed exit " + exit);
   }
   
   /** 
    * Remove a person from this place.  If there is a message, send it to 
    * everyone who is left in this place.  If the specified person is not here
    * this method does nothing and does not throw an exception.  This method
    * is called by go(), and the client should call it when the user quits.
    * The client should not allow the user to invoke it directly, however.
    **/
   public void exit(RemoteMudPerson who, String message)
 throws RemoteException
   {
       String name;
       synchronized(names) {
           int i = people.indexOf(who);
           if (i == -1) return;
           names.removeElementAt(i);
           people.removeElementAt(i);
       }
       if (message != null) tellEveryone(message);
   }
   
   /** 
    * This method puts a person into this place, assigning them the
    * specified name, and displaying a message to anyone else who is in
    * that place.  This method is called by go(), and the client should
    * call it to initially place a person into the MUD.  Once the person
    * is in the MUD, however, the client should restrict them to using go()
    * and should not allow them to call this method directly.
    * If there have been networking problems, a client might call this method
    * to restore a person to this place, in case they"ve been bumped out.
    * (A person will be bumped out of a place if the server tries to send
    * a message to them and gets a RemoteException.)
    **/
   public void enter(RemoteMudPerson who, String name, String message) 
 throws RemoteException, AlreadyThere
   {
       // Send the message to everyone who is already here.
       if (message != null) tellEveryone(message);
 
       // Add the person to this place.
       synchronized (names) {
           if (people.indexOf(who) != -1) throw new AlreadyThere();
           names.addElement(name);
           people.addElement(who);
       }
   }
   
   /**
    * This final remote method returns the server object for the MUD in which
    * this place exists.  The client should not allow the user to invoke this
    * method.
    **/
   public RemoteMudServer getServer() throws RemoteException {
 return server;
   }
   
   /** 
    * Create and start a thread that sends out a message everyone in this
    * place.  If it gets a RemoteException talking to a person, it silently
    * removes that person from this place.  This is not a remote method, but
    * is used internally by a number of remote methods.
    **/
   protected void tellEveryone(final String message) {
       // If there is no-one here, don"t bother sending the message!
       if (people.size() == 0) return;
       // Make a copy of the people here now.  The message is sent
       // asynchronously and the list of people in the room may change before
       // the message is sent to everyone.
       final Vector recipients = (Vector) people.clone();
       // Create and start a thread to send the message, using an anonymous
       // class.  We do this because sending the message to everyone in this
       // place might take some time, (particularly on a slow or flaky
       // network) and we don"t want to wait.
       new Thread() {
   public void run() {
       // Loop through the recipients
       for(int i = 0; i < recipients.size(); i++) {
     RemoteMudPerson person =
         (RemoteMudPerson)recipients.elementAt(i);
     // Try to send the message to each one.
     try { person.tell(message); } 
     // If it fails, assume that that person"s client or
     // network has failed, and silently remove them from
     // this place.
     catch (RemoteException e) { 
         try { MudPlace.this.exit(person, null); } 
         catch (Exception ex) {} 
     }
       }
   }
     }.start();
   }
   
   /**
    * This convenience method checks whether the specified person is here.
    * If so, it returns their name.  If not it throws a NotThere exception
    **/
   protected String verifyPresence(RemoteMudPerson who) throws NotThere {
       int i = people.indexOf(who);
       if (i == -1) throw new NotThere();
       else return (String) names.elementAt(i);
   }
   /**
    * This method is used for custom de-serialization.  Since the vectors of
    * people and of their names are transient, they are not serialized with
    * the rest of this place.  Therefore, when the place is de-serialized,
    * those vectors have to be recreated (empty).
    **/
   private void readObject(ObjectInputStream in) 
 throws IOException, ClassNotFoundException {
       in.defaultReadObject();  // Read most of the object as normal
       names = new Vector();    // Then recreate the names vector
       people = new Vector();   // and recreate the people vector
   }                     
   
   /** This constant is a version number for serialization */
   static final long serialVersionUID = 5090967989223703026L;

} /*

* Copyright (c) 2004 David Flanagan.  All rights reserved.
* This code is from the book Java Examples in a Nutshell, 3nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose,
* including teaching and use in open-source projects.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book, 
* please visit http://www.davidflanagan.ru/javaexamples3.
*/

package je3.rmi; import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; import java.io.*; import java.util.Hashtable; import java.util.zip.*; import je3.rmi.Mud.*; /**

* This class implements the RemoteMudServer interface.  It also defines a
* main() method so you can run it as a standalone program that will
* set up and initialize a MUD server.  Note that a MudServer maintains an
* entrance point to a MUD, but it is not the MUD itself.  Most of the 
* interesting MUD functionality is defined by the RemoteMudPlace interface
* and implemented by the RemotePlace class.  In addition to being a remote
* object, this class is also Serializable, so that the state of the MUD
* can be saved to a file and later restored.  Note that the main() method
* defines two ways of starting a MUD: one is to start it from scratch with
* a single initial place, and another is to restore an existing MUD from a
* file.
**/

public class MudServer extends UnicastRemoteObject

   implements RemoteMudServer, Serializable

{

   MudPlace entrance;  // The standard entrance to this MUD
   String password;    // The password required to dump() the state of the MUD
   String mudname;     // The name that this MUD is registered under
   Hashtable places;   // A mapping of place names to places in this MUD
   
   /**
    * Start a MUD from scratch, with the given name and password.  Create
    * an initial MudPlace object as the entrance, giving it the specified
    * name and description.
    **/
   public MudServer(String mudname, String password, 
        String placename, String description)
 throws RemoteException
   {
       this.mudname = mudname;
       this.password = password;
       this.places = new Hashtable();
       // Create the entrance place
       try { this.entrance = new MudPlace(this, placename, description); } 
       catch (PlaceAlreadyExists e) {} // Should never happen
   }
   
   /** For serialization only.  Never call this constructor. */
   public MudServer() throws RemoteException {}                   
   
   /** This remote method returns the name of the MUD */
   public String getMudName() throws RemoteException { return mudname; }
   
   /** This remote method returns the entrance place of the MUD */
   public RemoteMudPlace getEntrance() throws RemoteException { 
       return entrance; 
   }
   
   /**
    * This remote method returns a RemoteMudPlace object for the named place.
    * In this sense, a MudServer acts as like an RMI Registry object,
    * returning remote objects looked up by name.  It is simpler to do it this
    * way than to use an actual Registry object.  If the named place does not
    * exist, it throws a NoSuchPlace exception
    **/
   public RemoteMudPlace getNamedPlace(String name) 
             throws RemoteException, NoSuchPlace
   {
       RemoteMudPlace p = (RemoteMudPlace) places.get(name);
       if (p == null) throw new NoSuchPlace();
       return p;
   }
   
   /**
    * Define a new placename to place mapping in our hashtable.  
    * This is not a remote method.  The MudPlace() constructor calls it
    * to register the new place it is creating.
    **/
   public void setPlaceName(RemoteMudPlace place, String name) 
 throws PlaceAlreadyExists
   {
       if (places.containsKey(name)) throw new PlaceAlreadyExists();
       places.put(name, place);
   }
   
   /**
    * This remote method serializes and compresses the state of the MUD
    * to a named file, if the specified password matches the one specified
    * when the MUD was initially created.  Note that the state of a MUD
    * consists of all places in the MUD, with all things and exits in those
    * places.  The people in the MUD are not part of the state that is saved.
    **/
   public void dump(String password, String f) 
 throws RemoteException, BadPassword, IOException
   {
       if ((this.password != null)&& !this.password.equals(password)) 
           throw new BadPassword();
       ObjectOutputStream out = new ObjectOutputStream(
             new GZIPOutputStream(new FileOutputStream(f)));
       out.writeObject(this);
       out.close();
   }
   
   /**
    * This main() method defines the standalone program that starts up a MUD
    * server.  If invoked with a single argument, it treats that argument as
    * the name of a file containing the serialized and compressed state of an
    * existing MUD, and recreates it.  Otherwise, it expects four command-line
    * arguments: the name of the MUD, the password, the name of the entrance
    * place for the MUD, and a description of that entrance place.
    * Besides creating the MudServer object, this program sets an appropriate
    * security manager, and uses the default rmiregistry to register the
    * the MudServer under its given name.
    **/
   public static void main(String[] args) {
       try {
           MudServer server;
           if (args.length == 1) {
               // Read the MUD state in from a file
               FileInputStream f = new FileInputStream(args[0]);
               ObjectInputStream in =
       new ObjectInputStream(new GZIPInputStream(f));
               server = (MudServer) in.readObject();
           }
           // Otherwise, create an initial MUD from scratch
           else server = new MudServer(args[0], args[1], args[2], args[3]);
     
           Naming.rebind(Mud.mudPrefix + server.mudname, server);
       }
       // Display an error message if anything goes wrong.
       catch (Exception e) {
           System.out.println(e);
           System.out.println("Usage: java MudServer <savefile>\n" +
            "   or: java MudServer <mudname> <password> " + 
            "<placename> <description>");
           System.exit(1);
       }
   }
   /** This constant is a version number for serialization */
   static final long serialVersionUID = 7453281245880199453L;

} /*

* Copyright (c) 2004 David Flanagan.  All rights reserved.
* This code is from the book Java Examples in a Nutshell, 3nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose,
* including teaching and use in open-source projects.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book, 
* please visit http://www.davidflanagan.ru/javaexamples3.
*/

package je3.rmi; import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; import java.sql.*; import java.io.*; import java.util.*; import java.util.Date; // import explicitly to disambiguate from java.sql.Date import je3.rmi.Bank.*; // Import inner classes of Bank /**

* This class is another implementation of the RemoteBank interface.
* It uses a database connection as its back end, so that client data isn"t
* lost if the server goes down.  Note that it takes the database connection
* out of "auto commit" mode and explicitly calls commit() and rollback() to
* ensure that updates happen atomically.
**/

public class PersistentBankServer extends UnicastRemoteObject

   implements RemoteBank

{

   Connection db;   // The connection to the database that stores account info
   
   /** The constructor.  Just save the database connection object away */
   public PersistentBankServer(Connection db) throws RemoteException { 
       this.db = db;
   }
   
   /** Open an account */
   public synchronized void openAccount(String name, String password)
 throws RemoteException, BankingException
   {
       // First, check if there is already an account with that name
       Statement s = null;
       try { 
           s = db.createStatement();
           s.executeQuery("SELECT * FROM accounts WHERE name="" + name + """);
           ResultSet r = s.getResultSet();
           if (r.next()) throw new BankingException("Account name in use.");
     
           // If it doesn"t exist, go ahead and create it Also, create a
           // table for the transaction history of this account and insert an
           // initial transaction into it.
           s = db.createStatement();
           s.executeUpdate("INSERT INTO accounts VALUES ("" + name + "", "" +
         password + "", 0)");
           s.executeUpdate("CREATE TABLE " + name + 
         "_history (msg VARCHAR(80))");
           s.executeUpdate("INSERT INTO " + name + "_history " +
         "VALUES ("Account opened at " + new Date() + "")");
     
           // And if we"ve been successful so far, commit these updates,
           // ending the atomic transaction.  All the methods below also use
           // this atomic transaction commit/rollback scheme
           db.rumit();
       }
       catch(SQLException e) {
           // If an exception was thrown, "rollback" the prior updates,
           // removing them from the database.  This also ends the atomic
           // transaction.
           try { db.rollback(); } catch (Exception e2) {}
           // Pass the SQLException on in the body of a BankingException
           throw new BankingException("SQLException: " + e.getMessage() + 
              ": " + e.getSQLState());
       }
       // No matter what happens, don"t forget to close the DB Statement
       finally { try { s.close(); } catch (Exception e) {} }
   }
   
   /** 
    * This convenience method checks whether the name and password match
    * an existing account.  If so, it returns the balance in that account.
    * If not, it throws an exception.  Note that this method does not call
    * commit() or rollback(), so its query is part of a larger transaction.
    **/
   public int verify(String name, String password) 
 throws BankingException, SQLException
   {
       Statement s = null;
       try {
           s = db.createStatement();
           s.executeQuery("SELECT balance FROM accounts " +
        "WHERE name="" + name + "" " +
        "  AND password = "" + password + """);
           ResultSet r = s.getResultSet();
           if (!r.next())
               throw new BankingException("Bad account name or password");
           return r.getInt(1);
       }
       finally { try { s.close(); } catch (Exception e) {} }
   }
   
   /** Close a named account */
   public synchronized FunnyMoney closeAccount(String name, String password)
 throws RemoteException, BankingException
   {
       int balance = 0;
       Statement s = null;
       try {
           balance = verify(name, password);
           s = db.createStatement();
           // Delete the account from the accounts table
           s.executeUpdate("DELETE FROM accounts " + 
         "WHERE name = "" + name + "" " +
         "  AND password = "" + password + """);
           // And drop the transaction history table for this account
           s.executeUpdate("DROP TABLE " + name + "_history");
           db.rumit();
       }
       catch (SQLException e) {
           try { db.rollback(); } catch (Exception e2) {}
           throw new BankingException("SQLException: " + e.getMessage() + 
              ": " + e.getSQLState());
       }
       finally { try { s.close(); } catch (Exception e) {} }
 
       // Finally, return whatever balance remained in the account
       return new FunnyMoney(balance);
   }
   
   /** Deposit the specified money into the named account */
   public synchronized void deposit(String name, String password, 
            FunnyMoney money) 
 throws RemoteException, BankingException
   {
       int balance = 0; 
       Statement s = null;
       try {
           balance = verify(name, password);
           s = db.createStatement();
           // Update the balance
           s.executeUpdate("UPDATE accounts " +
         "SET balance = " + balance + money.amount + " " +
         "WHERE name="" + name + "" " +
         "  AND password = "" + password + """);
           // Add a row to the transaction history
           s.executeUpdate("INSERT INTO " + name + "_history " + 
         "VALUES ("Deposited " + money.amount + 
         " at " + new Date() + "")");
           db.rumit();
       }
       catch (SQLException e) {
           try { db.rollback(); } catch (Exception e2) {}
           throw new BankingException("SQLException: " + e.getMessage() + 
              ": " + e.getSQLState());
       }
       finally { try { s.close(); } catch (Exception e) {} }
   }
   
   /** Withdraw the specified amount from the named account */
   public synchronized FunnyMoney withdraw(String name, String password, 
             int amount)
 throws RemoteException, BankingException
   {
       int balance = 0;
       Statement s = null;
       try {
           balance = verify(name, password);
           if (balance < amount)
   throw new BankingException("Insufficient Funds");
           s = db.createStatement();
           // Update the account balance
           s.executeUpdate("UPDATE accounts " +
         "SET balance = " + (balance - amount) + " " +
         "WHERE name="" + name + "" " +
         "  AND password = "" + password + """);
           // Add a row to the transaction history
           s.executeUpdate("INSERT INTO " + name + "_history " + 
         "VALUES ("Withdrew " + amount + 
         " at " + new Date() + "")");
           db.rumit();
       }
       catch (SQLException e) {
           try { db.rollback(); } catch (Exception e2) {}
           throw new BankingException("SQLException: " + e.getMessage() + 
              ": " + e.getSQLState());
       }
       finally { try { s.close(); } catch (Exception e) {} }
 
       return new FunnyMoney(amount);
   }
   
   /** Return the balance of the specified account */
   public synchronized int getBalance(String name, String password)
 throws RemoteException, BankingException
   {
       int balance;
       try {
           // Get the balance
           balance = verify(name, password);
           // Commit the transaction
           db.rumit();
       }
       catch (SQLException e) {
           try { db.rollback(); } catch (Exception e2) {}
           throw new BankingException("SQLException: " + e.getMessage() + 
              ": " + e.getSQLState());
       }
       // Return the balance
       return balance;
   }
   
   /** Get the transaction history of the named account */
   public synchronized List getTransactionHistory(String name, 
              String password)
 throws RemoteException, BankingException
   {
       Statement s = null;
       List list = new ArrayList();
       try {
           // Call verify to check the password, even though we don"t 
           // care what the current balance is.
           verify(name, password);
           s = db.createStatement();
           // Request everything out of the history table
           s.executeQuery("SELECT * from " + name + "_history");
           // Get the results of the query and put them in a Vector
           ResultSet r = s.getResultSet();
           while(r.next()) list.add(r.getString(1));
           // Commit the transaction
           db.rumit();
       }
       catch (SQLException e) {
           try { db.rollback(); } catch (Exception e2) {}
           throw new BankingException("SQLException: " + e.getMessage() + 
              ": " + e.getSQLState());
       }
       finally { try { s.close(); } catch (Exception e) {} }
       // Return the Vector of transaction history.
       return list;
   }
   
   /**
    * This main() method is the standalone program that figures out what
    * database to connect to with what driver, connects to the database,
    * creates a PersistentBankServer object, and registers it with the registry,
    * making it available for client use
    **/
   public static void main(String[] args) {
       try {
           // Create a new Properties object.  Attempt to initialize it from
           // the BankDB.props file or the file optionally specified on the 
           // command line, ignoring errors.
           Properties p = new Properties();
           try { p.load(new FileInputStream(args[0])); }
           catch (Exception e) {
               try { p.load(new FileInputStream("BankDB.props")); }
               catch (Exception e2) {}
           }
     
           // The BankDB.props file (or file specified on the command line)
           // must contain properties "driver" and "database", and may
           // optionally contain properties "user" and "password".
           String driver = p.getProperty("driver");
           String database = p.getProperty("database");
           String user = p.getProperty("user", "");
           String password = p.getProperty("password", "");
     
           // Load the database driver class
           Class.forName(driver);
     
           // Connect to the database that stores our accounts
           Connection db = DriverManager.getConnection(database,
             user, password);
     
           // Configure the database to allow multiple queries and updates
           // to be grouped into atomic transactions
           db.setAutoCommit(false);
           db.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
     
           // Create a server object that uses our database connection
           PersistentBankServer bank = new PersistentBankServer(db);
     
           // Read a system property to figure out how to name this server.
           // Use "SecondRemote" as the default.
           String name = System.getProperty("bankname", "SecondRemote");
     
           // Register the server with the name
           Naming.rebind(name, bank);
     
           // And tell everyone that we"re up and running.
           System.out.println(name + " is open and ready for customers.");
       }
       catch (Exception e) {
           System.err.println(e);
           if (e instanceof SQLException) 
               System.err.println("SQL State: " +
          ((SQLException)e).getSQLState());
           System.err.println("Usage: java [-Dbankname=<name>] " +
           "je3.rmi.PersistentBankServer " +
            "[<dbpropsfile>]");
           System.exit(1);
       }
   }

} /*

* Copyright (c) 2004 David Flanagan.  All rights reserved.
* This code is from the book Java Examples in a Nutshell, 3nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose,
* including teaching and use in open-source projects.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book, 
* please visit http://www.davidflanagan.ru/javaexamples3.
*/

package je3.rmi; import java.rmi.*; import java.rmi.server.*; import java.util.*; import je3.rmi.Bank.*; /**

* This class implements the remote methods defined by the RemoteBank
* interface.  It has a serious shortcoming, though: all account data is
* lost when the server goes down.
**/

public class RemoteBankServer extends UnicastRemoteObject implements RemoteBank {

   /** 
    * This nested class stores data for a single account with the bank 
    **/
   class Account {
       String password;                      // account password
       int balance;                          // account balance
       List transactions = new ArrayList();  // account transaction history
       Account(String password) {
           this.password = password;
           transactions.add("Account opened at " + new Date());
       }
   }
   
   /** 
    * This hashtable stores all open accounts and maps from account name
    * to Account object.  Methods that use this object will be synchronized
    * to prevent concurrent access by more than one thread.
    **/
   Map accounts = new HashMap();
   
   /**
    * This constructor doesn"t do anything, but because the superclass 
    * constructor throws an exception, the exception must be declared here
    **/
   public RemoteBankServer() throws RemoteException { super(); }
   
   /** 
    * Open a bank account with the specified name and password 
    * This method is synchronized to make it thread safe, since it 
    * manipulates the accounts hashtable.
    **/
   public synchronized void openAccount(String name, String password)
 throws RemoteException, BankingException
   {
       // Check if there is already an account under that name
       if (accounts.get(name) != null) 
           throw new BankingException("Account already exists.");
       // Otherwise, it doesn"t exist, so create it.
       Account acct = new Account(password);
       // And register it
       accounts.put(name, acct);
   }
   
   /**
    * This internal method is not a remote method.  Given a name and password
    * it checks to see if an account with that name and password exists.  If
    * so, it returns the Account object.  Otherwise, it throws an exception.
    * This method is synchronized because it uses the accounts hashtable.
    **/
   synchronized Account verify(String name, String password)
       throws BankingException
   {
       Account acct = (Account)accounts.get(name);
       if (acct == null) throw new BankingException("No such account");
       if (!password.equals(acct.password))
           throw new BankingException("Invalid password");
       return acct;
   }
   
   /** 
    * Close the named account.  This method is synchronized to make it 
    * thread safe, since it manipulates the accounts hashtable.
    **/
   public synchronized FunnyMoney closeAccount(String name, String password)
 throws RemoteException, BankingException
   {
       Account acct;
       acct = verify(name, password);
       accounts.remove(name);
       // Before changing the balance or transactions of any account, we first
       // have to obtain a lock on that account to be thread safe.
       synchronized (acct) {
           int balance = acct.balance;
           acct.balance = 0;
           return new FunnyMoney(balance);
       }
   }
   
   /** Deposit the specified FunnyMoney to the named account */
   public void deposit(String name, String password, FunnyMoney money) 
 throws RemoteException, BankingException
   {
       Account acct = verify(name, password);
       synchronized(acct) { 
           acct.balance += money.amount; 
           acct.transactions.add("Deposited " + money.amount + 
         " on " + new Date());
       }
   }
   
   /** Withdraw the specified amount from the named account */
   public FunnyMoney withdraw(String name, String password, int amount)
 throws RemoteException, BankingException
   {
       Account acct = verify(name, password);
       synchronized(acct) {
           if (acct.balance < amount) 
               throw new BankingException("Insufficient Funds");
           acct.balance -= amount;
           acct.transactions.add("Withdrew " + amount + " on "+new Date());
           return new FunnyMoney(amount);
       }
   }
   
   /** Return the current balance in the named account */
   public int getBalance(String name, String password)
 throws RemoteException, BankingException
   {
       Account acct = verify(name, password);
       synchronized(acct) { return acct.balance; }
   }
   
   /** 
    * Return a Vector of strings containing the transaction history
    * for the named account
    **/
   public List getTransactionHistory(String name, String password)
 throws RemoteException, BankingException
   {
       Account acct = verify(name, password);
       synchronized(acct) { return acct.transactions; }
   }
   
   /**
    * The main program that runs this RemoteBankServer.
    * Create a RemoteBankServer object and give it a name in the registry.
    * Read a system property to determine the name, but use "FirstRemote"
    * as the default name.  This is all that is necessary to set up the
    * service.  RMI takes care of the rest.
    **/
   public static void main(String[] args) {
       try {
           // Create a bank server object
           RemoteBankServer bank = new RemoteBankServer();
           // Figure out what to name it
           String name = System.getProperty("bankname", "FirstRemote");
           // Name it that
           Naming.rebind(name, bank);
           // Tell the world we"re up and running
           System.out.println(name + " is open and ready for customers.");
       }
       catch (Exception e) {
           System.err.println(e);
           System.err.println("Usage: java [-Dbankname=<name>] " +
               "je3.rmi.RemoteBankServer");
           System.exit(1); // Force exit because there may be RMI threads
       }
   }

} /*

* Copyright (c) 2004 David Flanagan.  All rights reserved.
* This code is from the book Java Examples in a Nutshell, 3nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose,
* including teaching and use in open-source projects.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book, 
* please visit http://www.davidflanagan.ru/javaexamples3.
*/

package je3.rmi; import java.rmi.*; import java.util.List; /**

* This class is a placeholder that simply contains other classes and 
* for interfaces remote banking.
**/

public class Bank {

   /**
    * This is the interface that defines the exported methods of the 
    * bank server.
    **/
   public interface RemoteBank extends Remote {
       /** Open a new account, with the specified name and password */
       public void openAccount(String name, String password) 
     throws RemoteException, BankingException;
 
       /** Close the named account */
       public FunnyMoney closeAccount(String name, String password) 
     throws RemoteException, BankingException;
 
       /** Deposit money into the named account */
       public void deposit(String name, String password, FunnyMoney money)
     throws RemoteException, BankingException;
 
       /** Withdraw the specified amount of money from the named account */
       public FunnyMoney withdraw(String name, String password, int amount) 
     throws RemoteException, BankingException;
 
       /** Return the amount of money in the named account */
       public int getBalance(String name, String password) 
     throws RemoteException, BankingException;
 
       /** 
  * Return a List of Strings that list the transaction history 
  * of the named account 
  **/
       public List getTransactionHistory(String name, String password) 
     throws RemoteException, BankingException;
   }
   
   /**
    * This simple class represents a monetary amount.  This implementation
    * is really nothing more than a wrapper around an integer.  It is a useful
    * to demonstrate that RMI can accept arbitrary non-String objects as
    * arguments and return them as values, as long as they are Serializable.
    * A more complete implementation of this FunnyMoney class might bear
    * a serial number, a digital signature, and other security features to 
    * ensure that it is unique and non-forgeable.
    **/
   public static class FunnyMoney implements java.io.Serializable {
       public int amount;
       public FunnyMoney(int amount) { this.amount = amount; }
   }
   
   /**
    * This is a type of exception used to represent exceptional conditions
    * related to banking, such as "Insufficient Funds" and  "Invalid Password"
    **/
   public static class BankingException extends Exception {
       public BankingException(String msg) { super(msg); }
   }
   
   /**
    * This class is a simple stand-alone client program that interacts
    * with a RemoteBank server.  It invokes different RemoteBank methods
    * depending on its command-line arguments, and demonstrates just how
    * simple it is to interact with a server using RMI.
    **/
   public static class Client {
       public static void main(String[] args) {
           try {
               // Figure out what RemoteBank to connect to by reading a system
               // property (specified on the command line with a -D option to
               // java) or, if it is not defined, use a default URL.  Note
               // that by default this client tries to connect to a server on
               // the local machine
               String url = System.getProperty("bank", "rmi:///FirstRemote");
   
               // Now look up that RemoteBank server using the Naming object,
               // which contacts the rmiregistry server.  Given the url, this
               // call returns a RemoteBank object whose methods may be
               // invoked remotely
               RemoteBank bank = (RemoteBank) Naming.lookup(url);
               
               // Convert the user"s command to lower case
               String cmd = args[0].toLowerCase();
   
               // Now, go test the command against a bunch of possible options
               if (cmd.equals("open")) {           // Open an account
                   bank.openAccount(args[1], args[2]);
                   System.out.println("Account opened.");
               }
               else if (cmd.equals("close")) {     // Close an account
                   FunnyMoney money = bank.closeAccount(args[1], args[2]);
                   // Note: our currency is denominated in wooden nickels
                   System.out.println(money.amount +
              " wooden nickels returned to you.");
                   System.out.println("Thanks for banking with us.");
               }
               else if (cmd.equals("deposit")) {   // Deposit money
                   FunnyMoney money=new FunnyMoney(Integer.parseInt(args[3]));
                   bank.deposit(args[1], args[2], money);
                   System.out.println("Deposited " + money.amount +
              " wooden nickels.");
               }
               else if (cmd.equals("withdraw")) {  // Withdraw money
                   FunnyMoney money = bank.withdraw(args[1], args[2], 
               Integer.parseInt(args[3]));
                   System.out.println("Withdrew " + money.amount +
              " wooden nickels.");
               }
               else if (cmd.equals("balance")) {   // Check account balance
                   int amt = bank.getBalance(args[1], args[2]);
                   System.out.println("You have " + amt +
              " wooden nickels in the bank.");
               }
               else if (cmd.equals("history")) {   // Get transaction history
                   List transactions =
     bank.getTransactionHistory(args[1], args[2]);
                   for(int i = 0; i < transactions.size(); i++)
                       System.out.println(transactions.get(i));
               }
               else System.out.println("Unknown command");
           }
           // Catch and display RMI exceptions
           catch (RemoteException e) { System.err.println(e); }
           // Catch and display Banking related exceptions
           catch (BankingException e) { System.err.println(e.getMessage()); }
           // Other exceptions are probably user syntax errors, so show usage.
           catch (Exception e) { 
               System.err.println(e);
               System.err.println("Usage: java [-Dbank=<url>] Bank$Client " + 
          "<cmd> <name> <password> [<amount>]");
               System.err.println("where cmd is: open, close, deposit, " + 
          "withdraw, balance, history");
           }
       }
   }

} /*

* Copyright (c) 2004 David Flanagan.  All rights reserved.
* This code is from the book Java Examples in a Nutshell, 3nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose,
* including teaching and use in open-source projects.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book, 
* please visit http://www.davidflanagan.ru/javaexamples3.
*/

package je3.rmi; import java.rmi.*; import java.util.Vector; import java.io.IOException; /**

* This class defines three nested Remote interfaces for use by our MUD game.
* It also defines a bunch of exception subclasses, and a constant string
* prefix used to create unique names when registering MUD servers
**/

public class Mud {

   /**
    * This interface defines the exported methods of the MUD server object
    **/
   public interface RemoteMudServer extends Remote {
       /** Return the name of this MUD */
       public String getMudName() throws RemoteException;
 
       /** Return the main entrance place for this MUD */
       public RemoteMudPlace getEntrance() throws RemoteException; 
 
       /** Look up and return some other named place in this MUD */
       public RemoteMudPlace getNamedPlace(String name) 
     throws RemoteException, NoSuchPlace;
 
       /** 
  * Dump the state of the server to a file so that it can be restored
  * later All places, and their exits and things are dumped, but the
  * "people" in them are not.
  **/
       public void dump(String password, String filename) 
     throws RemoteException, BadPassword, IOException;
   }
   
   /**
    * This interface defines the methods exported by a "person" object that
    * is in the MUD.
    **/
   public interface RemoteMudPerson extends Remote {
       /** Return a full description of the person */
       public String getDescription() throws RemoteException;
 
       /** Deliver a message to the person */
       public void tell(String message) throws RemoteException;
   }
   
   /**
    * This is the most important remote interface for the MUD.  It defines the
    * methods exported by the "places" or "rooms" within a MUD.  Each place
    * has a name and a description, and also maintains a list of "people" in
    * the place, things in the place, and exits from the place.  There are
    * methods to get a list of names for these people, things, and exits.
    * There are methods to get the RemoteMudPerson object for a named person,
    * to get a description of a named thing, and to go through a named exit.
    * There are methods for interacting with other people in the MUD.  There
    * are methods for building the MUD by creating and destroying things,
    * adding new places (and new exits to those places), for linking a place
    * through a new exit to some other place (possibly on another MUD server),
    * and for closing down an existing exit.
    **/
   public interface RemoteMudPlace extends Remote {
       /** Look up the name of this place */
       public String getPlaceName() throws RemoteException;
 
       /** Get a description of this place */
       public String getDescription() throws RemoteException;
 
       /** Find out the names of all people here */
       public Vector getNames() throws RemoteException;
 
       /** Get the names of all things here */
       public Vector getThings() throws RemoteException;
 
       /** Get the names of all ways out of here */
       public Vector getExits() throws RemoteException;
 
       /** Get the RemoteMudPerson object for the named person. */
       public RemoteMudPerson getPerson(String name) 
     throws RemoteException, NoSuchPerson;
 
       /** Get more details about a named thing */
       public String examineThing(String name)
     throws RemoteException,NoSuchThing;
 
       /** Use the named exit */
       public RemoteMudPlace go(RemoteMudPerson who, String direction) 
     throws RemoteException,NotThere,AlreadyThere,NoSuchExit,LinkFailed;
 
       /** Send a message of the form "David: hi everyone" */
       public void speak(RemoteMudPerson speaker, String msg) 
     throws RemoteException, NotThere;
 
       /** Send a message of the form "David laughs loudly" */
       public void act(RemoteMudPerson speaker, String msg) 
     throws RemoteException, NotThere;
 
       /** Add a new thing in this place */
       public void createThing(RemoteMudPerson who, String name, 
       String description) 
     throws RemoteException, NotThere, AlreadyThere;
 
       /** Remove a thing from this place */
       public void destroyThing(RemoteMudPerson who, String thing) 
     throws RemoteException, NotThere, NoSuchThing;
 
       /**
  * Create a new place, bi-directionally linked to this one by an exit
  **/
       public void createPlace(RemoteMudPerson creator, 
       String exit, String entrance,
       String name, String description) 
     throws RemoteException,NotThere,
      ExitAlreadyExists,PlaceAlreadyExists;
       /** 
  * Link this place (unidirectionally) to some existing place.  The
  * destination place may even be on another server.
  **/
       public void linkTo(RemoteMudPerson who, String exit, 
        String hostname, String mudname, String placename) 
     throws RemoteException, NotThere, ExitAlreadyExists, NoSuchPlace;
 
       /** Remove an existing exit */
       public void close(RemoteMudPerson who, String exit) 
     throws RemoteException, NotThere, NoSuchExit;
 
       /** 
  * Remove this person from this place, leaving them nowhere.
  * Send the specified message to everyone left in the place.
  **/
       public void exit(RemoteMudPerson who, String message) 
     throws RemoteException, NotThere;
 
       /**
  * Put a person in a place, assigning their name, and sending the 
  * specified message to everyone else in the place.  The client should
  * not make this method available to the user.  They should use go()
  * instead.
  **/
       public void enter(RemoteMudPerson who, String name, String message) 
     throws RemoteException, AlreadyThere;
 
       /** 
  * Return the server object of the MUD that "contains" this place 
  * This method should not be directly visible to the player
  **/
       public RemoteMudServer getServer() throws RemoteException;
   }
   
   /**
    * This is a generic exception class that serves as the superclass
    * for a bunch of more specific exception types 
    **/
   public static class MudException extends Exception {}
   
   /**
    * These specific exception classes are thrown in various contexts.
    * The exception class name contains all the information about the 
    * exception; no detail messages are provided by these classes.
    **/
   public static class NotThere extends MudException {}
   public static class AlreadyThere extends MudException {}
   public static class NoSuchThing extends MudException {}
   public static class NoSuchPerson extends MudException {}
   public static class NoSuchExit extends MudException {}
   public static class NoSuchPlace extends MudException {}
   public static class ExitAlreadyExists extends MudException {}
   public static class PlaceAlreadyExists extends MudException {}
   public static class LinkFailed extends MudException {}
   public static class BadPassword extends MudException {}
   
   /**
    * This constant is used as a prefix to the MUD name when the server
    * registers the mud with the RMI Registry, and when the client looks 
    * up the MUD in the registry.  Using this prefix helps prevent the 
    * possibility of name collisions.
    **/
   static final String mudPrefix = "je3.rmi.Mud.";

}


      </source>
   
  
 
  



RMIBroadCast.zip

RMI Demo

   <source lang="java">

/* From http://java.sun.ru/docs/books/tutorial/index.html */ /*

* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* -Redistribution of source code must retain the above copyright notice, this
*  list of conditions and the following disclaimer.
*
* -Redistribution in binary form must reproduce the above copyright notice,
*  this list of conditions and the following disclaimer in the documentation
*  and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
          
      </source>
   
  
 
  



RMI example

Use RMI to compute

   <source lang="java">

/*

* Copyright (c) 1995 - 2008 Sun Microsystems, Inc.  All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   - Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   - Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in the
*     documentation and/or other materials provided with the distribution.
*
*   - Neither the name of Sun Microsystems nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ 

package client; import compute.Task; import java.io.Serializable; import java.math.BigDecimal; public class Pi implements Task<BigDecimal>, Serializable {

   private static final long serialVersionUID = 227L;
   /** constants used in pi computation */
   private static final BigDecimal FOUR =
       BigDecimal.valueOf(4);
   /** rounding mode to use during pi computation */
   private static final int roundingMode = 
       BigDecimal.ROUND_HALF_EVEN;
   /** digits of precision after the decimal point */
   private final int digits;
   
   /**
    * Construct a task to calculate pi to the specified
    * precision.
    */
   public Pi(int digits) {
       this.digits = digits;
   }
   /**
    * Calculate pi.
    */
   public BigDecimal execute() {
       return computePi(digits);
   }
   /**
    * Compute the value of pi to the specified number of 
    * digits after the decimal point.  The value is 
    * computed using Machin"s formula:
    *
    *          pi/4 = 4*arctan(1/5) - arctan(1/239)
    *
    * and a power series expansion of arctan(x) to 
    * sufficient precision.
    */
   public static BigDecimal computePi(int digits) {
       int scale = digits + 5;
       BigDecimal arctan1_5 = arctan(5, scale);
       BigDecimal arctan1_239 = arctan(239, scale);
       BigDecimal pi = arctan1_5.multiply(FOUR).subtract(
                                 arctan1_239).multiply(FOUR);
       return pi.setScale(digits, 
                          BigDecimal.ROUND_HALF_UP);
   }
   /**
    * Compute the value, in radians, of the arctangent of 
    * the inverse of the supplied integer to the specified
    * number of digits after the decimal point.  The value
    * is computed using the power series expansion for the
    * arc tangent:
    *
    * arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 + 
    *     (x^9)/9 ...
    */   
   public static BigDecimal arctan(int inverseX, 
                                   int scale) 
   {
       BigDecimal result, numer, term;
       BigDecimal invX = BigDecimal.valueOf(inverseX);
       BigDecimal invX2 = 
           BigDecimal.valueOf(inverseX * inverseX);
       numer = BigDecimal.ONE.divide(invX,
                                     scale, roundingMode);
       result = numer;
       int i = 1;
       do {
           numer = 
               numer.divide(invX2, scale, roundingMode);
           int denom = 2 * i + 1;
           term = 
               numer.divide(BigDecimal.valueOf(denom),
                            scale, roundingMode);
           if ((i % 2) != 0) {
               result = result.subtract(term);
           } else {
               result = result.add(term);
           }
           i++;
       } while (term.rupareTo(BigDecimal.ZERO) != 0);
       return result;
   }

} /*

* Copyright (c) 1995 - 2008 Sun Microsystems, Inc.  All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   - Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   - Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in the
*     documentation and/or other materials provided with the distribution.
*
*   - Neither the name of Sun Microsystems nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ 

package client; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.math.BigDecimal; import compute.rupute; public class ComputePi {

   public static void main(String args[]) {
       if (System.getSecurityManager() == null) {
           System.setSecurityManager(new SecurityManager());
       }
       try {
           String name = "Compute";
           Registry registry = LocateRegistry.getRegistry(args[0]);
           Compute comp = (Compute) registry.lookup(name);
           Pi task = new Pi(Integer.parseInt(args[1]));
           BigDecimal pi = comp.executeTask(task);
           System.out.println(pi);
       } catch (Exception e) {
           System.err.println("ComputePi exception:");
           e.printStackTrace();
       }
   }    

} /*

* Copyright (c) 1995 - 2008 Sun Microsystems, Inc.  All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   - Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   - Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in the
*     documentation and/or other materials provided with the distribution.
*
*   - Neither the name of Sun Microsystems nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ 

package compute; public interface Task<T> {

   T execute();

} /*

* Copyright (c) 1995 - 2008 Sun Microsystems, Inc.  All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   - Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   - Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in the
*     documentation and/or other materials provided with the distribution.
*
*   - Neither the name of Sun Microsystems nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ 

package compute; import java.rmi.Remote; import java.rmi.RemoteException; public interface Compute extends Remote {

   <T> T executeTask(Task<T> t) throws RemoteException;

} /*

* Copyright (c) 1995 - 2008 Sun Microsystems, Inc.  All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   - Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   - Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in the
*     documentation and/or other materials provided with the distribution.
*
*   - Neither the name of Sun Microsystems nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ 

package engine; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import compute.rupute; import compute.Task; public class ComputeEngine implements Compute {

   public ComputeEngine() {
       super();
   }
   public <T> T executeTask(Task<T> t) {
       return t.execute();
   }
   public static void main(String[] args) {
       if (System.getSecurityManager() == null) {
           System.setSecurityManager(new SecurityManager());
       }
       try {
           String name = "Compute";
           Compute engine = new ComputeEngine();
           Compute stub =
               (Compute) UnicastRemoteObject.exportObject(engine, 0);
           Registry registry = LocateRegistry.getRegistry();
           registry.rebind(name, stub);
           System.out.println("ComputeEngine bound");
       } catch (Exception e) {
           System.err.println("ComputeEngine exception:");
           e.printStackTrace();
       }
   }

} File: client.policy grant codeBase "file:/home/jones/src/" {

   permission java.security.AllPermission;

};

File: server.policy grant codeBase "file:/home/ann/src/" {

   permission java.security.AllPermission;

};


      </source>