Mega Code Archive

 
Categories / Java / Network Protocol
 

RMI based Bank system

/*  * 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.com/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.com/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.com/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.com/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.com/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.commit();         }         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.commit();         }         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.commit();         }         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.commit();         }         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.commit();         }         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.commit();         }         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.com/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.com/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.com/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."; }                     rmiDemo.zip( 25 k)