21.2 A Bank Server

Example 21-1 defined a RemoteBank interface and a bank client program. Example 21-2 is a RemoteBankServer class that implements the RemoteBank interface and acts as a server for the Bank.Client program. This class includes a main( ) method so it can be run as a standalone program. This method creates a RemoteBankServer object and registers it with Naming.rebind( ), so that clients can look it up. It reads the system property bankname to determine what name to use to register the bank, but uses the name FirstRemote by default. (This is the same name that the Bank.Client uses by default as well.)

RemoteBankServer implements the RemoteBank interface, so it provides implementations for all remote methods defined by that interface. It also defines some utility methods that are not remote methods, but that are used by the remote methods. Note that RemoteBankServer includes an inner Account class that stores all the information about a single bank account. It maintains a hashtable that maps from account names to Account objects. The various remote methods look up the named account, verify the password, and operate on the account in some way. Any RMI remote object must be able to handle multiple, concurrent method invocations because multiple clients can be using the object at the same time. RemoteBankServer uses synchronized methods and synchronized statements to prevent two clients from opening, closing, or modifying the same account at the same time.

Before you can run this RemoteBankServer program, you must compile it, generate stub and skeleton classes, and start the rmiregistry service (if it is not already running). You might do all this with commands like the following (on a Unix system). Note the -d argument to rmic : it tells the RMI compiler where to put the stub and skeleton classes. Assuming the RemoteBankServer.class file is in the current directory, the usage shown here puts the generated classes in the same directory.

% javac RemoteBankServer.java % rmic -d ../../../../ je3.rmi.RemoteBankServer % rmiregistry & % java je3.rmi.RemoteBankServer FirstRemote is open and ready for customers.

Note that Example 21-2 contains a fatal flaw: if the bank server crashes, all bank account data is lost, which is likely to result in angry customers! Example 21-3 is another implementation of the RemoteBank interface; this implementation uses a database to store account data in a more persistent way.

Example 21-2. RemoteBankServer.java
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         }     } }


Java Examples in a Nutshell
Java Examples in a Nutshell, 3rd Edition
ISBN: 0596006209
EAN: 2147483647
Year: 2003
Pages: 285

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net