Without the Persistence Library

With the Persistence Library

/*
 * BankExampleWithoutDatabase.java
 *
 * Copyright (C) 2004-08 Side of Software (SOS)
 * All rights reserved.
 *
 *    http://www.sideofsoftware.com
 */
 
package examples;
 
 
import java.util.*;
 
 
/**
 * A sample program with Side of Software's
 * Persistence Library.
 *
 * @author  Side of Software
 */
public class BankExampleWithoutDatabase
{
  private static final int NUM_ACCOUNTS = 10;
  private static final int NUM_REPORTS = 10;
  private static final int NUM_DEPOSITS = 10;
  private static final int NUM_TRANSFERS = 10;
  private static final int NUM_DEPOSIT_THREADS = 5;
  private static final int NUM_TRANSFER_THREADS = 5;
  private static final int NUM_REPORT_THREADS = 10;
  private static final int INITIAL_BALANCE = 100;
  private static final int AMOUNT_TO_DEPOSIT = 10;
  private static final int AMOUNT_TO_TRANSFER = 20;
 
 
 
  
  /** A mapping from account number to account.*/
  private Map accounts;
  
  public BankExampleWithoutDatabase()
  {
 
 
 
 
 
 
 
 
 
 

    accounts = new HashMap();

    
    // create the accounts
 
    for( int i = 0; i < NUM_ACCOUNTS; i++ )
      openAccount( i, INITIAL_BALANCE );
 
  }
  
  /**
   * Returns a new thread that repeatedly deposits
   * to all of the accounts.<p>
   *
   * @param depositAmount amount of each deposit
   * @return the new thread that makes deposits
   */  
  private Thread createDepositThread( final double
    depositAmount )
  {
    Thread thread = new Thread( new Runnable() {
      public void run()
      {
        for( int acctNumber = 0; 
          acctNumber < NUM_ACCOUNTS; 
          acctNumber++ )
        {
          Account acct
            = (Account)lookupAccount( acctNumber );
 
          for( int i = 0; i < NUM_DEPOSITS; i++ )
          {
            deposit( acct, depositAmount );
          }
        }
      }
    } );
    return thread;
  }
  
  /**
   * Returns a new thread that repeatedly
   * sums the balances of all accounts.<p>
   *
   * @return the new thread to sum the balances
   */  
  private Thread createReportThread()
  {
    Thread thread = new Thread( new Runnable() {
      public void run()
      {
        for( int i = 0; i < NUM_REPORTS; i++ )
        {
          generateReport();
        }
      }
    } );
    return thread;
  }
  
  /**
   * Returns a new thread that repeatedly
   * transfers from one account to the next.<p>
   *
   * @param transferAmount amount of each deposit
   * @return the new thread that makes deposits
   */  
  private Thread createTransferThread( 
    final double transferAmount )
  {
    Thread thread = new Thread( new Runnable() {
      public void run()
      {
        for( int acctNumber = 0; 
          acctNumber < NUM_ACCOUNTS; acctNumber++ )
        {
          Account fromAcct = lookupAccount( acctNumber );
          Account toAcct = lookupAccount( 
            ( acctNumber + 1 ) % NUM_ACCOUNTS );
 
          for( int i = 0; i < NUM_TRANSFERS; i++ )
          {
            transfer( fromAcct, toAcct, transferAmount );
          }
        }
      }
    } );
    return thread;
  }
  
  /**
   * Deposits the specified amount
   * to the specified account.<p>
   *
   * @param acct account to add or subtract money
   * @param amount amount to deposit (may be negative)
   */  
  private synchronized void deposit( 
    Account acct, double amount )
  {
 
    double balance = acct.getBalance();
    balance += amount;
    acct.setBalance( balance );
 
  }
 
  /**
   * Iterates through all accounts,
   * summing the balances.<p>
   */
  private synchronized void generateReport()
  {
 
 
    int numAccounts = accounts.size();
    double total = 0;
    
    // for each account
    Collection values = accounts.values();
    Iterator acctIterator = values.iterator();
    while( acctIterator.hasNext() )
    {
      Account account = (Account)acctIterator.next();
      double balance = account.getBalance();
      total += balance;
    }
    
 
 
    // print the report
    System.err.println( total + " for "
      + numAccounts + " accounts " );
  }
  
  /**
   * Finds the account with the specified account
   * number. Returns <code>null</code>
   * if no account is found.<p>
   *
   * @param accountNumber number of account to find
   * @return the account with the specified number
   *    or <code>null</code> if none found
   */  
  private synchronized Account lookupAccount( 
    int accountNumber )
  {
    Object key = new Integer( accountNumber );
    return (Account)accounts.get( key );
  }
  
  /**
   * Serves as the entry point for the program.<p>
  *
   * @param args ignored
   */
  public static void main(String[] args)
    throws InterruptedException
 
  {
 
 
 
 
    BankExampleWithoutDatabase example
      = new BankExampleWithoutDatabase();
    example.run();
  }
  
  /**
   * Adds a new account with the specified 
   * number and initial balance.<p>
   *
   * @param accountNumber account number of account
   * @param initialBalance initial balance of account
   * @return the new account
   */  
  private synchronized Account openAccount( 
    int accountNumber, double initialBalance )
  {
 
 
    // create the account
    Account account = new DefaultAccount();
 
 
    // store account in map indexed by number
    Object key = new Integer( accountNumber );
    accounts.put( key, account );
   
    // set the initial balance of this account
    account.setBalance( initialBalance );
 
   
    return account;
  }
 
  /**
   * Runs the sample program.<p>
   */
  public void run()
    throws InterruptedException
  {
    // create threads that will test the system
    int numThreads = NUM_DEPOSIT_THREADS
      + NUM_TRANSFER_THREADS + NUM_REPORT_THREADS;
    Thread[] threads = new Thread[numThreads];
    
    int index = 0;
    
    // create threads that repeatedly deposit
    for( int i = 0; i < NUM_DEPOSIT_THREADS; i++ )
      threads[index++]
        = createDepositThread( AMOUNT_TO_DEPOSIT );
    
    // create threads that repeatedly transfer
    for( int i = 0; i < NUM_TRANSFER_THREADS; i++ )
      threads[index++]
        = createTransferThread( AMOUNT_TO_TRANSFER );
    
    // create threads that repeatedly report totals
    for( int i = 0; i < NUM_REPORT_THREADS; i++ )
      threads[index++] = createReportThread();
 
    // start the threads
    for( int i = 0; i < threads.length; i++ )
      threads[i].start();
    
    // wait until the threads stop
    for( int i = 0; i < threads.length; i++ )
      threads[i].join();
    
    // verify the account balances
    double expectedBalance = INITIAL_BALANCE
      + NUM_DEPOSIT_THREADS * NUM_DEPOSITS
      * AMOUNT_TO_DEPOSIT;
    for( int i = 0; i < NUM_ACCOUNTS; i++ )
    {
      Account acct = lookupAccount( i );
      double balance = acct.getBalance();
      assert balance == expectedBalance :
        "Incorrect balance for account " + i + ": "
        + balance;
    }
 
 
 
  }
  
  /**
   * Transfers the specified amount from 
   * one account to another.<p>
   *
   * @param fromAcct account to transfer from
   * @param toAcct account to transfer to
   * @param amount the amount to transfer
   */  
  private synchronized void transfer( Account fromAcct,
    Account toAcct, double amount )
  {
 
    deposit( fromAcct, -amount );
    deposit( toAcct, amount );
 
  }
}
/*
 * BankExampleWithDatabase.java
 *
 * Copyright (C) 2004-08 Side of Software (SOS)
 * All rights reserved.
 *
 *    http://www.sideofsoftware.com
 */
 
package examples;
 

import java.io.*;

import java.util.*;

import sos.db.*;

 
/**
 * A sample program without Side of Software's
 * Persistence Library.
 *
 * @author  Side of Software
 */
public class BankExampleWithDatabase
{
  private static final int NUM_ACCOUNTS = 10;
  private static final int NUM_REPORTS = 10;
  private static final int NUM_DEPOSITS = 10;
  private static final int NUM_TRANSFERS = 10;
  private static final int NUM_DEPOSIT_THREADS = 5;
  private static final int NUM_TRANSFER_THREADS = 5;
  private static final int NUM_REPORT_THREADS = 10;
  private static final int INITIAL_BALANCE = 100;
  private static final int AMOUNT_TO_DEPOSIT = 10;
  private static final int AMOUNT_TO_TRANSFER = 20;
  

  /** The underlying database. */

  private Database db;

  
  /** A mapping from account number to account.*/
  private Map accounts;
 
  public BankExampleWithDatabase() throws IOException
  {

    // construct the database

    db = new InMemoryDatabase( "Account Database" );

   

    // initialize database with map as the root

    db.create( new HashMap() );

   

    // open the database

    db.open( false );

   

    // load the root object

    accounts = (Map)db.getRoot();

 
    // create the accounts

    db.startTransaction();

    for( int i = 0; i < NUM_ACCOUNTS; i++ )
      openAccount( i, INITIAL_BALANCE );

    db.commitTransaction( null );

  }
  
  /**
   * Returns a new thread that repeatedly deposits
   * to all of the accounts.<p>
   *
   * @param depositAmount amount of each deposit
   * @return the new thread that makes deposits
   */  
  private Thread createDepositThread( final double
      depositAmount )
  {
    Thread thread = new Thread( new Runnable() {
      public void run()
      {
        for( int acctNumber = 0;
          acctNumber < NUM_ACCOUNTS; 
          acctNumber++ )
        {
          Account acct
            = (Account)lookupAccount( acctNumber );
 
          for( int i = 0; i < NUM_DEPOSITS; i++ )
          {
            deposit( acct, depositAmount );
          }
        }
      }
    } );
    return thread;
  }
  
  /**
   * Returns a new thread that repeatedly
   * sums the balances of all accounts.<p>
   *
   * @return the new thread to sum the balances
   */  
  private Thread createReportThread()
  {
    Thread thread = new Thread( new Runnable() {
      public void run()
      {
        for( int i = 0; i < NUM_REPORTS; i++ )
        {
          generateReport();
        }
      }
    } );
    return thread;
  }
  
  /**
   * Returns a new thread that repeatedly
   * transfers from one account to the next.<p>
  *
   * @param transferAmount amount of each deposit
   * @return the new thread that makes deposits
   */  
  private Thread createTransferThread( 
    final double transferAmount )
  {
    Thread thread = new Thread( new Runnable() {
      public void run()
      {
        for( int acctNumber = 0; 
          acctNumber < NUM_ACCOUNTS; acctNumber++ )
        {
          Account fromAcct = lookupAccount( acctNumber );
          Account toAcct = lookupAccount( 
            ( acctNumber + 1 ) % NUM_ACCOUNTS );
 
          for( int i = 0; i < NUM_TRANSFERS; i++ )
          {
            transfer( fromAcct, toAcct, transferAmount );
          }
        }
      }
    } );
    return thread;
  }
  
  /**
   * Deposits the specified amount
   * to the specified account.<p>
   *
   * @param acct account to add or subtract money
   * @param amount amount to deposit (may be negative)
   */  
  private void deposit( 
    Account acct, double amount )
  {

    db.startTransaction();

    double balance = acct.getBalance();
    balance += amount;
    acct.setBalance( balance );

    db.commitTransaction( null );

  }
 
  /**
   * Iterates through all accounts,
   * summing the balances.<p>
   */
  private void generateReport()
  {
    db.startTransaction();
 
    int numAccounts = accounts.size();
    double total = 0;
    
    // for each account
    Collection values = accounts.values();
    Iterator acctIterator = values.iterator();
    while( acctIterator.hasNext() )
    {
      Account account = (Account)acctIterator.next();
      double balance = account.getBalance();
      total += balance;
    }
    

    db.commitTransaction( null );

    
    // print the report
    System.err.println( total + " for "
      + numAccounts + " accounts " );
  }
  
  /**
   * Finds the account with the specified account
   * number. Returns <code>null</code>
   * if no account is found.<p>
   *
   * @param accountNumber number of account to find
   * @return the account with the specified number
   *    or <code>null</code> if none found
   */  
  private Account lookupAccount( 
    int accountNumber )
  {
    Object key = new Integer( accountNumber );
    return (Account)accounts.get( key );
  }
  
  /**
   * Serves as the entry point for the program.<p>
   *
   * @param args ignored
   */
  public static void main(String[] args)
    throws InterruptedException, IOException,
           NoSuchMethodException
  {

    // indicate getBalance does not modify the account

    ReadOnlyManager.setMethodStatus( Account.class,

      "getBalance", null, ReadOnlyManager.READ_ONLY );

    
    BankExampleWithDatabase example
      = new BankExampleWithDatabase();
    example.run();
  }
  
  /**
   * Adds a new account with the specified 
   * number and initial balance.<p>
   *
   * @param accountNumber account number of account
   * @param initialBalance initial balance of account
   * @return the new account
   */  
  private Account openAccount( 
    int accountNumber, double initialBalance )
  {

    db.startTransaction();

    
    // create the account
    Account account = new DefaultAccount();

    account = (Account)db.addObject( account );

    
    // store account in map indexed by number
    Object key = new Integer( accountNumber );
    accounts.put( key, account );
    
    // set the initial balance of this account
    account.setBalance( initialBalance );
    

    db.commitTransaction( null );

    return account;
  }
 
  /**
   * Runs the sample program.<p>
   */
  public void run()
    throws InterruptedException, IOException
  {
    // create threads that will test the system
    int numThreads = NUM_DEPOSIT_THREADS
      + NUM_TRANSFER_THREADS + NUM_REPORT_THREADS;
    Thread[] threads = new Thread[numThreads];
    
    int index = 0;
    
    // create threads that repeatedly deposit
    for( int i = 0; i < NUM_DEPOSIT_THREADS; i++ )
      threads[index++]
        = createDepositThread( AMOUNT_TO_DEPOSIT );
    
    // create threads that repeatedly transfer
    for( int i = 0; i < NUM_TRANSFER_THREADS; i++ )
      threads[index++]
        = createTransferThread( AMOUNT_TO_TRANSFER );
    
    // create threads that repeatedly report totals
    for( int i = 0; i < NUM_REPORT_THREADS; i++ )
      threads[index++] = createReportThread();
 
    // start the threads
    for( int i = 0; i < threads.length; i++ )
      threads[i].start();
    
    // wait until the threads stop
    for( int i = 0; i < threads.length; i++ )
      threads[i].join();
    
    // verify the account balances
    double expectedBalance = INITIAL_BALANCE
      + NUM_DEPOSIT_THREADS * NUM_DEPOSITS
      * AMOUNT_TO_DEPOSIT;
    for( int i = 0; i < NUM_ACCOUNTS; i++ )
    {
      Account acct = lookupAccount( i );
      double balance = acct.getBalance();
      assert balance == expectedBalance : 
        "Incorrect balance for account " + i + ": "
        + balance;
    }
    

    // close the database

    db.close();

  }
  
  /**
   * Transfers the specified amount from 
   * one account to another.<p>
   *
   * @param fromAcct account to transfer from
   * @param toAcct account to transfer to
   * @param amount the amount to transfer
   */  
  private void transfer( Account fromAcct,
    Account toAcct, double amount )
  {

    db.startTransaction();

    deposit( fromAcct, -amount );
    deposit( toAcct, amount );

    db.commitTransaction( null );

  }
}