FileDocCategorySizeDatePackage
RemoteBankServer.javaAPI DocExample5860Mon Sep 22 13:30:32 BST 1997None

RemoteBankServer.java

// This example is from _Java Examples in a Nutshell_. (http://www.oreilly.com)
// Copyright (c) 1997 by David Flanagan
// This example is provided WITHOUT ANY WARRANTY either expressed or implied.
// You may study, use, modify, and distribute it for non-commercial purposes.
// For any commercial use, see http://www.davidflanagan.com/javaexamples

import java.rmi.*;
import java.rmi.server.*;
import java.util.*;
import 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
    Vector transactions = new Vector();   // account transaction history
    Account(String password) {
      this.password = password;
      transactions.addElement("Account opened at " + new Date());
    }
  }
  
  /** 
   * This hashtable stores all open accounts and maps from account name
   * to Account object
   **/
  Hashtable accounts = new Hashtable();
  
  /**
   * This constructor doesn't do anything, but because it throws the
   * same exception that the superclass constructor throws, it must be
   * declared.
   **/
  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 convenience 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.
   **/
  public Account verify(String name, String password) 
       throws BankingException {
    synchronized(accounts) {
      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.addElement("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.addElement("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 Vector 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>] RemoteBankServer");
      System.exit(1); // Force an exit because there might be other threads
    }
  }
}