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