Apache Financial Services

Simple Bank

The Simple Bank implementation aims to establish a minimal implementation - unsecured, very simplistic, but functional.

Block Directive

Bank has no service dependencies (after all, its a rather simple implementation). The block.xml simply establishes the Bank instance ready to handle account creation and removal requests.

<container name="banking">

  <component name="bank" class="org.apache.bank.impl.BankProvider" activation="startup"/>

</container>

Bank Implementation

BankProvider is a simple component with dependencies on the supplied runtime context. It uses the supplied context to retrieve a working directory from which it can internalise and externalise a serializable set of accounts. With an established set of accounts, the Bank can handle requests for new account creation and account removal.

Bank.java

package org.apache.bank.impl;

import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;

import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.activity.Disposable;

import org.apache.bank.Bank;
import org.apache.bank.Account;
import org.apache.bank.PolicyException;
import org.apache.bank.NoSuchAccountException;


public class BankProvider extends AbstractLogEnabled 
  implements Contextualizable, Initializable, Disposable, Bank
{
    private File m_home = null;

   /**
    * The persistant account registry.
    */
    private Accounts m_accounts;

   /**
    * Supply of the runtime context to the component by the container.  The 
    * component uses the supplied home directory to store accounts.
    * @param context the runtime context
    * @exception ContextException if the container does not supply the 
    *    home directory under the "urn:avalon:home" key.
    */
    public void contextualize( Context context ) throws ContextException
    {
        m_home = (File) context.get( "urn:avalon:home" );
        getLogger().info( "setting home directory: " + m_home );
    }

   /**
    * Initialization of the component by the container.
    * @exception if an initialization error occurs
    */
    public void initialize() throws Exception
    {
        File file = new File( m_home, "accounts.ser" );
        getLogger().info( "initialization" );
        if( file.exists() )
        {
            getLogger().info( "loading store: " + file );
            InputStream stream = new FileInputStream( file );
            final ObjectInputStream ois = new ObjectInputStream( stream );
            m_accounts = (Accounts) ois.readObject();
            getLogger().info( "accounts established: " + m_accounts.accounts() );
        }
        else
        {
            getLogger().info( "creating new account registry" );
            m_accounts = new Accounts();
        }
    }

   /**
    * Disposal of the bank by the container.
    */
    public void dispose()
    {
        getLogger().info( "initiating bank disposal" );
        try
        {
            File file = new File( m_home, "accounts.ser" );
            File parent = file.getParentFile();
            parent.mkdirs();
            file.createNewFile();
            FileOutputStream stream = new FileOutputStream( file );
            final ObjectOutputStream output = new ObjectOutputStream( stream );
            output.writeObject( m_accounts );
            output.flush();
            getLogger().info( "accounts saved to " + file );
        }
        catch( Throwable e )
        {
            final String error = 
             "Unable to write accounts to file.";
            getLogger().error( error, e );
        }
    }

   /**
    * Get the number of accounts managed by the bank.
    * @return the number of accounts
    */
    public int accounts()
    {
        return m_accounts.accounts();
    }

   /**
    * Create, register and return a new account.
    * @param name the name of the account holder
    * @return Account the account
    */
    public Account createAccount( String name )
    {
        Account account = m_accounts.createAccount( name );
        getLogger().info( 
          "created new account for: " + name 
          + " with id: " + account.getID() );
        return account;
    }

   /**
    * Close an account.
    * @param id the account number
    */
    public void closeAccount( int id ) throws PolicyException, NoSuchAccountException
    {
        getLogger().info( "closing account: " + id );
        m_accounts.closeAccount( id );
    }

   /**
    * Returns an account based on a supplied account number.
    * @param id the account number
    * @return Account the bank account
    */
    public Account getAccount( int id ) throws NoSuchAccountException
    {
        Account account = m_accounts.getAccount( id );
        getLogger().info( 
          "retrieving account: " + id  
          + " for " + account.getName() );
        return account;
    }
}

Bank Implementation

The DefaultAccount implementation provides support for functional operations including the deposit and withdrawal of funds and provision of the standing account balance.

Bank.java

package org.apache.bank.impl;

import java.io.Serializable;

import org.apache.bank.Account;
import org.apache.bank.InsufficientFundsException;

public class DefaultAccount implements Account, Serializable
{
    private int m_id;
    private String m_name;
    private float m_balance;

    public DefaultAccount( String name, int id )
    {
        m_id = id;
        m_name = name;
    }

   /**
    * Deposit funds into the account.
    * @param amount the amount of funds to deposit
    */
    public synchronized void deposit( float amount )
    {
        m_balance = m_balance + amount;
    }

   /**
    * Withdraw funds from the account.
    * @param amount the amount of funds to withdraw
    */
    public synchronized void withdraw( float amount ) throws InsufficientFundsException
    {
        if( amount > m_balance )
        {
            final String message = 
              "Request withdrawl of " + amount 
              + " exceeds balance of " + m_balance + ".";
            throw new InsufficientFundsException( message );
        }
        m_balance = m_balance - amount;
    }

   /**
    * Get the account balance.
    * @return the account balance
    */
    public float getBalance()
    {
        return m_balance;
    }

   /**
    * Get the account name.
    * @return the account name
    */
    public String getName()
    {
        return m_name;
    }

   /**
    * Get the account number.
    * @return the account number
    */
    public int getID()
    {
        return m_id;
    }

    public int hashcode()
    {
        return getID();
    }
}