FileDocCategorySizeDatePackage
RemoteDBBankServer.javaAPI DocExample11548Mon Sep 22 13:30:32 BST 1997None

RemoteDBBankServer

public class RemoteDBBankServer extends UnicastRemoteObject implements RemoteBank
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.

Fields Summary
Connection
db
Constructors Summary
public RemoteDBBankServer(Connection db)
The constructor. Just save the database connection object away

 
    this.db = db;
  
Methods Summary
public synchronized FunnyMoneycloseAccount(java.lang.String name, java.lang.String password)
Close a named account

    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);
  
public synchronized voiddeposit(java.lang.String name, java.lang.String password, FunnyMoney money)
Deposit the specified money into the named account

    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) {} }
  
public synchronized intgetBalance(java.lang.String name, java.lang.String password)
Return the balance of the specified account

    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;
  
public synchronized java.util.VectorgetTransactionHistory(java.lang.String name, java.lang.String password)
Get the transaction history of the named account

    Statement s = null;
    Vector v = new Vector();
    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()) v.addElement(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 v;
  
public static voidmain(java.lang.String[] args)
This main() method is the standalone program that figures out what database to connect to with what driver, connects to the database, creates a RemoteDBBankServer object, and registers it with the registry, making it available for client use

    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
      RemoteDBBankServer bank = new RemoteDBBankServer(db);

      // Read a system property to figure out how to name this server.
      // Use "SecondRemote" as the default.
      String name = System.getProperty("bankname", "SecondRemote");

      // Register the server with the name
      Naming.rebind(name, bank);

      // And tell everyone that we're up and running.
      System.out.println(name + " is open and ready for customers.");
    }
    catch (Exception e) {
      System.err.println(e);
      if (e instanceof SQLException) 
        System.err.println("SQL State: " + ((SQLException)e).getSQLState());
      System.err.println("Usage: java [-Dbankname=<name>] RemoteDBBankServer "+
                         "[<dbpropsfile>]");
      System.exit(1);
    }
  
public synchronized voidopenAccount(java.lang.String name, java.lang.String password)
Open an account

    // 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 already exists.");

      // 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) {} }
  
public intverify(java.lang.String name, java.lang.String password)
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.

    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("No such account or invalid password");
      return r.getInt(1);
    }
    finally { try { s.close(); } catch (Exception e) {} }
  
public synchronized FunnyMoneywithdraw(java.lang.String name, java.lang.String password, int amount)
Withdraw the specified amount from the named account

    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);