RMI公司的银行体系 : RMI « 网络协议 « Java

En
Java
1. 图形用户界面
2. 三维图形动画
3. 高级图形
4. 蚂蚁编译
5. Apache类库
6. 统计图
7. 
8. 集合数据结构
9. 数据类型
10. 数据库JDBC
11. 设计模式
12. 开发相关类
13. EJB3
14. 电子邮件
15. 事件
16. 文件输入输出
17. 游戏
18. 泛型
19. GWT
20. Hibernate
21. 本地化
22. J2EE平台
23. 基于J2ME
24. JDK-6
25. JNDI的LDAP
26. JPA
27. JSP技术
28. JSTL
29. 语言基础知识
30. 网络协议
31. PDF格式RTF格式
32. 映射
33. 常规表达式
34. 脚本
35. 安全
36. Servlets
37. Spring
38. Swing组件
39. 图形用户界面
40. SWT-JFace-Eclipse
41. 线程
42. 应用程序
43. Velocity
44. Web服务SOA
45. 可扩展标记语言
Java 教程
Java » 网络协议 » RMI屏幕截图 
RMI公司的银行体系

/*
 * 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 > 2placename = 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 == nulllocation = server.getEntrance();
            else location = (RemoteMudPlaceserver.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 == nullarg = "";
    
                // 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 (instanceof NoSuchThing
                    System.out.println("There isn't any such thing here.")
                else if (instanceof NoSuchPerson
                   System.out.println("There isn't anyone by that name here.");
                else if (instanceof NoSuchExit
                  System.out.println("There isn't an exit in that direction.");
                else if (instanceof NoSuchPlace
                    System.out.println("There isn't any such place.")
                else if (instanceof ExitAlreadyExists)
                    System.out.println("There is already an exit " +
               "in that direction.");
                else if (instanceof PlaceAlreadyExists)
                    System.out.println("There is already a place " +
               "with that name.");
                else if (instanceof LinkFailed)
                    System.out.println("That exit is not functioning.");
                else if (instanceof BadPassword
                    System.out.println("Invalid password.")
                else if (instanceof NotThere)      // Shouldn't happen
                    System.out.println("You can't do that when " +
               "you're not there.")
                else if (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 > 0System.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 > 0System.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 > 0System.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 != nullline = 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() 0return 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 messagethrows 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 == -1throw new NoSuchPerson();
            return (RemoteMudPersonpeople.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 namethrows RemoteException, NoSuchThing
    {
        synchronized(things) {
            int i = things.indexOf(name);
            if (i == -1throw new NoSuchThing();
            return (Stringdescriptions.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 == -1throw 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 = (Stringdestination;
                int pos = t.indexOf('@');
                String url = t.substring(0, pos);
                String placename = t.substring(pos+1);
                RemoteMudServer s = (RemoteMudServerNaming.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 = (RemoteMudPlacedestination;
  
        // 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!= -1throw 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 == -1throw 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!= -1throw 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 = (RemoteMudServerNaming.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!= -1throw 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 == -1throw 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 == -1return;
            names.removeElementAt(i);
            people.removeElementAt(i);
        }
        if (message != nulltellEveryone(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 != nulltellEveryone(message);
  
        // Add the person to this place.
        synchronized (names) {
            if (people.indexOf(who!= -1throw 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() == 0return;
        // 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 = (Vectorpeople.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 whothrows NotThere {
        int i = people.indexOf(who);
        if (i == -1throw new NotThere();
        else return (Stringnames.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 = (RemoteMudPlaceplaces.get(name);
        if (p == nullthrow 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 = (MudServerin.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 dbthrows 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 (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 == nullthrow 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 = (RemoteBankNaming.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 messagethrows 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)
Related examples in the same category
1. 使用RMI的计算
2. RMIBroadCast.zip
3. RMI的实例
4. RMI的演示
www.java2java.com | Contact Us
Copyright 2010 - 2030 Java Source and Support. All rights reserved.
All other trademarks are property of their respective owners.