Day 8 Adding Automated Teller Machine (ATM) Capability

<script LANGUAGE="JavaScript"> </script>


Teach Yourself CORBA In 14 Days

Previous chapterNext chapterContents


Day 8
Adding Automated Teller Machine (ATM) Capability

 


On Day 7, you started with the basic Bank application and made the first set of enhancements to it, adding error-checking functionality through the CORBA exception mechanism. The end result was an application with the same basic functionality that you implemented on Day 6 but with more robust error handling. Today you'll build on that same application, adding more features. Specifically, you'll define the interface for a virtual Automated Teller Machine (ATM) device and enhance the Bank application to interact with this device. The process you'll follow is this:

  • Define additional requirements. The original requirements for the Bank application said nothing about ATMs, so you will modify the requirements to define this new feature.

  • Modify the system design. Adding ATM functionality will result in a number of new classes being added to the system. You'll identify these classes in the analysis and design phase. (Revisiting this phase will reinforce the concept that software design is an iterative process.)

  • Modify the IDL definitions. After you have defined the new classes to be added and have modified some existing classes, you'll create or update the interface definitions for those classes.

  • Implement the new functionality. After the design is finished and realized in IDL, you'll be ready to implement the new functionality and see how the new application works.

Defining Additional Requirements

First of all, as a review, recall that on Day 5 you defined the following system requirements for the Bank application:

  • Supports multiple banks

  • Supports multiple accounts within banks

  • Supports multiple customers holding accounts

  • Supports customers holding multiple accounts

  • Supports the capability to open (create) new accounts

  • Supports the capability to close (delete) existing accounts

  • Supports the capability to enumerate a bank's accounts

  • Supports the capability to determine an account's owner(s)

  • Supports the capability to withdraw funds from an account

  • Supports the capability to deposit funds into an account

  • Supports the capability to transfer funds between accounts within a single bank

  • Supports the capability to transfer funds between accounts in different banks

  • Supports checking accounts (which don't gain interest)

  • Supports savings accounts (which do gain interest)

The original requirements said nothing about the support of Automated Teller Machines (ATMs). But what if you want to modify the application to support the ATM concept? Certainly it is possible to add this capability to the application; this chapter demonstrates just how to do this.

To add ATM capability, you first define a set of requirements that describes exactly what functionality is necessary. To support ATM functionality, the requirements might look something like this:

  • Supports the capability for a customer to deposit funds into an existing account through an ATM

  • Supports the capability for a customer to withdraw funds from an existing account through an ATM

  • Supports the capability for a customer to obtain the balance of an existing account through an ATM

  • Supports the capability for an ATM to authorize customers through the use of an ATM card and a Personal Identification Number (PIN)

  • Supports the capability for an ATM card to access multiple accounts belonging to a customer in a particular bank

In other words, an ATM provides three basic functions: depositing funds, withdrawing funds, and obtaining account balances. The ATM is accessed via an ATM card that, in conjunction with the customer's PIN, authorizes that customer to access the account (or accounts) for which the ATM card is authorized.

Of course, you won't be dealing with an actual ATM; rather, you'll be simulating the operation of an ATM through IDL interfaces. The operation of the ATM will thus be greatly simplified compared to an actual ATM, which must interface with a cash dispenser, video screen, keypad input, network, and other components. Your ATM interface will abstract the entire ATM, assuming that these components all work together as a single unit.

Modifying the Class Diagram

Now that you've identified the requirements associated with providing ATM functionality, you're ready to decide which existing classes in the system need to be modified, which classes need to be added, and what the new classes should do.

Two New Classes: ATM and ATMCard

Naturally, the expansion of the Bank application to include ATM functionality introduces some new classes into the system. An obvious class is the ATM itself; another class is ATMCard--the device the customer uses to identify himself or herself to the ATM. Now, take a look at the operations that these classes must support. Recall that the ATM needs to support deposit, withdraw, and account balance operations, in addition to authenticating the customer through the ATM card. ATMCard, in turn, needs to indicate the accounts that it is authorized to use, ought to support the capability to add and remove accounts to and from its list of authorized accounts, and--for the purposes of this application, anyway--must include the PIN associated with that ATMCard as a member data item.

The interface for the ATM class looks like this:

name : string

withdraw(card : ATMCard, account : Account, pin : integer,

        amount : float) : float

deposit(card : ATMCard, account : Account, pin : integer,

        amount : float) : float

getBalance(card : ATMCard, account : Account, pin : integer)

        : float

Note that each of the operations in the ATM class requires an ATMCard to identify the customer and the Accounts for which he or she is authorized access, an Account on which to operate, and a pin (which for the purposes of this application is simply an integer) to authenticate the customer.

ATMCard's interface resembles the following:

pin : integer

getAccounts() : Account[]

addAccount(account : Account) : void

removeAccount(account : Account) : void

isAuthorized(account : Account) : boolean

Modifying Existing Classes

Of course, you can't just create the ATM and ATMCard classes and expect the ATM capability to be integrated with the rest of the application; you must also modify some of the existing classes. For example, an ATMCard has to come from somewhere; typically, it is issued by a Bank. Also, a Customer would have some knowledge of the ATMCard because the ATMCard is intended to be used by Customers in the first place. Finally, a Customer should be able to locate and access ATMs, so it would be reasonable to modify BankServer to provide locations of ATMs as well as Banks.

First, consider the Bank class, which must be able to issue ATMCards to Customers. A reasonable way to achieve this functionality is to add the following method to Bank:

issueATMCard(pin : integer, account : Account) : ATMCard

Here, it is assumed that when the ATMCard is issued, an initial pin will be set; that ATMCard will also be authorized initially to access the given Account. Note that for this application, it is the responsibility of the caller (presumably a Customer) to retain the ATMCard after it is created.

Customer, again, should be modified to become aware of ATMCards so that a Customer can make use of them. It turns out that the only addition required to Customer is an internal list of the ATMCards held by that Customer; for the sake of convenience, you might add a method that enables other objects to access that list of ATMCards. However, for the purposes of this application, you can assume that other objects don't need access to this information (besides, people don't usually share information about their ATM cards with other people!). For your purposes, no changes are required to the Customer interface itself (changes to the Customer implementation come later).

Finally, BankServer should be modified to facilitate access to ATMs as well as Banks. Recall the operations provided in the BankServer interface that enable access to Banks:

registerBank(bank : Bank) : void

unregisterBank(bank : Bank) : void

getBanks() : Bank[]

It seems reasonable to mimic this interface for ATMs, yielding the following operations:

registerATM(atm : ATM) : void

unregisterATM(atm : ATM) : void

getATMs() : ATM[]

The modified class diagram--expanded to include the new ATM and ATMCard classes, along with the modified Bank class--appears in Figure 8.1.

Figure 8.1. The modified Bank application class diagram.

Two New Exceptions: AuthorizationException and InvalidATMException

The operations in the ATM class suggest the need for a new exception. To meet this need, define a new exception (call it AuthorizationException) that can be thrown by any of the ATM methods. Later, you'll implement these methods to raise an AuthorizationException if there is a problem authenticating the customer (for example, the customer-supplied PIN doesn't match the pin stored in the ATMCard) or authorizing the customer (for example, if the Account passed to a method in ATM is not authorized on the given ATMCard).

Also, recall the new methods added to the BankServer interface. Because they essentially duplicate the Bank-related operations, which raise InvalidBankExceptions, it follows that there ought to be an analogous exception for the ATM-related operations. Therefore, you'll want to define the InvalidATMException, which is used just as you might expect.

Modifying the IDL Specification

The modification of the IDL specifications is straightforward. Start with the modified Bank interface, which appears in Listing 8.1 with changes highlighted in bold.

Listing 8.1. Modified Bank.idl.
 1: // Bank.idl

 2: 

 3: // Forward declaration of Bank interface.

 4: interface Bank;

 5: 

 6: #ifndef Bank_idl

 7: #define Bank_idl

 8: 

 9: // sequence of Banks

10: typedef sequence<Bank> BankList;

11: 

12: #include "Customer.idl"

13: #include "Account.idl"

14: #include "ATMCard.idl"

15: #include "Exceptions.idl"

16: 

17: // A Bank provides access to Accounts. It can create an Account

18: // on behalf of a Customer, delete an Account, or list the current

19: // Accounts with the Bank.

20: interface Bank {

21: 

22:     // This Bank's name.

23:     attribute string name;

24: 

25:     // This Bank's address.

26:     attribute string address;

27: 

28:     // Create an Account on behalf of the given Customer, with the

29:     // given account type ("savings" or "checking", where case is

30:     // significant), and the given opening balance.

31:     Account createAccount(in Customer customer, in string

32:             accountType, in float openingBalance);

33: 

34:     // Delete the given Account. If the Account is not with this

35:     // Bank, this operation does nothing.

36:     void deleteAccount(in Account account)

37:             raises (InvalidAccountException);

38: 

39:     // List all Accounts with this Bank.

40:     AccountList getAccounts();

41: 

42:     // Issue an ATMCard with the initial PIN and initially

43:     // authorized on the given Account. If the Account is not with

44:     // this Bank, this operation does nothing.

45:     ATMCard issueATMCard(in short pin, in Account account)

46:             raises (InvalidAccountException);

47: };

48: 

49: #endif 

Turn your attention to the modified Exceptions.idl, which now contains the newly created AuthorizationException and InvalidATMException. The modified file appears in Listing 8.2.

Listing 8.2. Modified Exceptions.idl.
 1: // Exceptions.idl

 2: 

 3: #ifndef Exceptions_idl

 4: #define Exceptions_idl

 5: 

 6: // This exception is thrown when an authorization fails; for

 7: // instance, if a Customer-entered PIN does not match the PIN for

 8: // the supplied ATMCard.

 9: exception AuthorizationException {

10: 

11: };

12: 

13: // This exception is thrown when an invalid amount is passed to a

14: // method; for instance, if an account is asked to deposit a

15: // negative amount of funds.

16: exception InvalidAmountException {

17: 

18: };

19: 

20: // This exception is thrown when an invalid Account is passed to a

21: // method expecting an Account object.

22: exception InvalidAccountException {

23: 

24: };

25: 

26: // This exception is thrown when an invalid Bank is passed to a

27: // method expecting a Bank object.

28: exception InvalidBankException {

29: 

30: };

31: 

32: // This exception is thrown when an invalid ATM is passed to a

33: // method expecting an ATM object.

34: exception InvalidATMException {

35: 

36: };

37: 

38: // This exception is thrown when there are insufficient funds to

39: // cover a transaction; for instance, if a withdrawal attempts to

40: // remove more funds than are available in an account.

41: exception InsufficientFundsException {

42: 

43: };

44: 

45: #endif 

Now consider the IDL mappings for the newly created classes ATM and ATMCard, which appear in Listings 8.3 and 8.4. There are no surprises in the IDL interface definitions; all member data and methods map to IDL as you might expect.

Listing 8.3. ATM.idl.
 1: // ATM.idl

 2: 

 3: // Forward declaration of ATM interface.

 4: interface ATM;

 5: 

 6: #ifndef ATM_idl

 7: #define ATM_idl

 8: 

 9: // sequence of ATMs

10: typedef sequence<ATM> ATMList;

11: 

12: #include "Account.idl"

13: #include "ATMCard.idl"

14: #include "Exceptions.idl"

15: 

16: // An ATM is used to (indirectly) access Accounts. Each operation

17: // through the ATM is verified through the ATMCard given; for

18: // example, an Account being operated on must be authorized by the

19: // given ATMCard.

20: interface ATM {

21: 

22:     // This ATM's name.

23:     attribute string name;

24: 

25:     // Withdraw the given amount from the given Account. Returns

26:     // the new account balance. If the given ATMCard is not

27:     // authorized on the given Account, or the given PIN does not

28:     // match the ATMCard's PIN, this operation does nothing.

29:     float withdraw(in ATMCard card, in Account account, in short

30:             pin, in float amount) raises (AuthorizationException,

31:             InvalidAmountException, InsufficientFundsException);

32: 

33:     // Deposit the given amount into the given Account. Returns the

34:     // new account balance. If the given ATMCard is not authorized

35:     // on the given Account, or the given PIN does not match the

36:     // ATMCard's PIN, this operation does nothing.

37:     float deposit(in ATMCard card, in Account account, in short

38:             pin, in float amount) raises (AuthorizationException,

39:             InvalidAmountException);

40: 

41:     // Return the current balance of the given Account. If the

42:     // given ATMCard is not authorized on the given Account, or the

43:     // given PIN does not match the ATMCard's PIN, this operation

44:     // does nothing.

45:     float getBalance(in ATMCard card, in Account account, in short

46:             pin) raises (AuthorizationException);

47: };

48: 

49: #endif
Listing 8.4. ATMCard.idl.
 1: // ATMCard.idl

 2: 

 3: // Forward declaration of ATMCard interface.

 4: interface ATMCard;

 5: 

 6: #ifndef ATMCard_idl

 7: #define ATMCard_idl

 8: 

 9: // sequence of ATMCards

10: typedef sequence<ATMCard> ATMCardList;

11: 

12: #include "Account.idl"

13: #include "Exceptions.idl"

14: 

15: // An ATMCard is used to access an ATM. It maintains a list of

16: // Accounts which it is authorized to access, as well as a PIN

17: // which must be provided when the ATMCard is used.

18: interface ATMCard {

19: 

20:     // This ATMCard's PIN.

21:     attribute short pin;

22: 

23:     // List all Accounts which this ATMCard is authorized to use.

24:     AccountList getAccounts();

25: 

26:     // Add the given Account to the list of Accounts which this

27:     // ATMCard is authorized to use.

28:     void addAccount(in Account account)

29:             raises (InvalidAccountException);

30: 

31:     // Remove the given Account from the list of Accounts which

32:     // this ATMCard is authorized to use.

33:     void removeAccount(in Account account)

34:             raises (InvalidAccountException);

35: 

36:     // Return true if the given Account is authorized on this

37:     // ATMCard.

38:     boolean isAuthorized(in Account account);

39: };

40: 

41: #endif 

This concludes the analysis and design portion of the ATM functionality enhancements; you're now ready to begin implementing the changes in the code itself.

Implementing the New Functionality

To implement the ATM-related enhancements, you must modify the implementation files to provide the new functionality. As a quick overview, the changes you need to make are as follows:

  • Enhance the BankServer. Essentially, everything that the BankServer currently does for Banks, you will extend it to do for ATMs as well.

  • Enhance the Bank. In addition to requiring the capability to issue ATMCards, the Bank also provides the implementation for the ATMCard as well.

  • Implement the ATM server. Essentially, the ATM accepts transaction requests from Customers, validates the Customer's authorization against an ATMCard, and, if authorized, forwards the requests to the appropriate Accounts.

  • Implement the ATM client. This application is an extension of the previous Bank client, modified to create an Account and subsequently access that Account through an ATM rather than directly.

Enhancing the BankServer

The first step in the implementation of ATM functionality is to enhance the BankServer to support ATMs as well as Banks. Because the support is exactly the same for each type of object (for example, ATMs and Banks can both register and unregister with the BankServer), the ATM-related methods can be copied almost directly from their Bank-related counterparts. The necessary modifications to the BankServer implementation files appear in Listings 8.5-8.7, with changes from the previous version highlighted in bold.

Listing 8.5. BankServerImpl.h.
 1: // BankServerImpl.h

 2: 

 3: #ifndef BankServerImpl_h

 4: #define BankServerImpl_h

 5: 

 6: #include <vector>

 7: 

 8: #include "../BankServer_s.h"

 9: 

10: class BankServerImpl : public _sk_BankServer {

11: 

12: public:

13: 

14:     // Constructor.

15:     BankServerImpl(const char* name);

16: 

17:     // Destructor.

18:     ~BankServerImpl();

19: 

20:     // These methods are described in BankServer.idl.

21:     virtual void registerBank(Bank_ptr bank) throw

22:             (InvalidBankException);

23:     virtual void unregisterBank(Bank_ptr bank) throw

24:             (InvalidBankException);

25:     virtual BankList* getBanks();

26:     virtual void registerATM(ATM_ptr atm) throw

27:             (InvalidATMException);

28:     virtual void unregisterATM(ATM_ptr atm) throw

29:             (InvalidATMException);

30:     virtual ATMList* getATMs();

31: 

32: private:

33: 

34:     // Default constructor.

35:     BankServerImpl();

36: 

37:     // This BankServer's list of Banks.

38:     std::vector<Bank_ptr> myBanks;

39: 

40:     // This BankServer's list of ATMs.

41:     std::vector<ATM_ptr> myATMs;

42: };

43: 

44: #endif 

In Listing 8.5, note the similarity between the registerBank() method (lines 21-22) and the registerATM() method (lines 26-27), as well as the other pairs of corresponding methods. Also, just as the BankServerImpl uses a std::vector<Bank_ptr> to store references to Bank objects (as seen in lines 37-38), it now uses a std::vector<ATM_ptr> to store references to ATM objects (lines 40-41).

Listing 8.6 illustrates the further similarities between the previously existing method implementations in BankServerImpl and the ATM-related methods being added. The implementations are exactly the same, with references to Banks changed to references to ATMs, and so on. (Because the semantics of the operations are the same for ATMs as for Banks, it isn't surprising that the implementations are nearly identical.)

Listing 8.6. BankServerImpl.cpp.
  1: // BankServerImpl.cpp

  2: 

  3: #include "BankServerImpl.h"

  4: 

  5: #include <algorithm>

  6: #include <functional>

  7: 

  8: // STL-derived unary function which returns TRUE if Banks are equal.

  9: class IsBankEqual : public std::unary_function<Bank_ptr, bool> {

 10: public:

 11:     IsBankEqual(argument_type bank) { myBank = bank; }

 12:     result_type operator()(argument_type bank) { return bank->

 13:             _is_equivalent(myBank) != 0; }

 14: private:

 15:     argument_type myBank;

 16: };

 17: 

 18: // STL-derived unary function which returns TRUE if ATMs are equal.

 19: class IsATMEqual : public std::unary_function<ATM_ptr, bool> {

 20: public:

 21:     IsATMEqual(argument_type atm) { myATM = atm; }

 22:     result_type operator()(argument_type atm) { return atm->

 23:             _is_equivalent(myATM) != 0; }

 24: private:

 25:     argument_type myATM;

 26: };

 27: 

 28: // Constructor.

 29: BankServerImpl::BankServerImpl(const char* name) :

 30:         _sk_BankServer(name), myBanks(), myATMs() {

 31: 

 32: }

 33: 

 34: // Destructor.

 35: BankServerImpl::~BankServerImpl() {

 36: 

 37: }

 38: 

 39: void BankServerImpl::registerBank(Bank_ptr bank) throw

 40:         (InvalidBankException) {

 41: 

 42:     // First, ensure that the given Bank doesn't exist already.

 43:     std::vector<Bank_ptr>::iterator first = myBanks.begin();

 44:     std::vector<Bank_ptr>::iterator last = myBanks.end();

 45:     IsBankEqual predicate(bank);

 46: 

 47:     std::vector<Bank_ptr>::iterator matchedBank = std::

 48:             find_if(first, last, predicate);

 49:     if (matchedBank == last) {

 50: 

 51:         // Bank was not found, so add the given Bank to the end of

 52:         // the list.

 53:         cout << "BankServerImpl: Registering Bank /"" << bank->

 54:                 name() << "/"." << endl;

 55:         myBanks.push_back(Bank::_duplicate(bank));

 56:         return;

 57:     } else {

 58: 

 59:         // The Bank was already registered, so throw an exception.

 60:         throw InvalidBankException();

 61:   }

 62: }

 63: 

 64: void BankServerImpl::unregisterBank(Bank_ptr bank) throw

 65:         (InvalidBankException) {

 66: 

 67:     std::vector<Bank_ptr>::iterator first = myBanks.begin();

 68:     std::vector<Bank_ptr>::iterator last = myBanks.end();

 69:     IsBankEqual predicate(bank);

 70: 

 71:     std::vector<Bank_ptr>::iterator matchedBank = std::

 72:             find_if(first, last, predicate);

 73:     if (matchedBank == last) {

 74: 

 75:         // Invalid Bank; throw an exception.

 76:         cout << "BankServerImpl: Attempted to unregister invalid "

 77:                 "Bank." << endl;

 78:         throw InvalidBankException();

 79:     }

 80:     cout << "BankServerImpl: Unregistering Bank /"" << bank->name()

 81:             << "/"." << endl;

 82: 

 83:     // Delete the given Bank.

 84:     myBanks.erase(matchedBank);

 85:     bank->_release();

 86: }

 87: 

 88: BankList* BankServerImpl::getBanks() {

 89: 

 90:     BankList* list = new BankList();

 91:     CORBA::Long i;

 92: 

 93:     list->length(myBanks.size());

 94:     for (i = 0; i < myBanks.size(); i++) {

 95:         (*list)[i] = Bank::_duplicate(myBanks[i]);

 96:     }

 97: 

 98:     cout << "BankServerImpl: Returning list of " << myBanks.size()

 99:             << " Banks." << endl;

100: 

101:     return BankList::_duplicate(list);

102: }

103: 

104: void BankServerImpl::registerATM(ATM_ptr atm) throw

105:         (InvalidATMException) {

106: 

107:     // First, ensure that the given ATM doesn't exist already.

108:     std::vector<ATM_ptr>::iterator first = myATMs.begin();

109:     std::vector<ATM_ptr>::iterator last = myATMs.end();

110:     IsATMEqual predicate(atm);

111: 

112:     std::vector<ATM_ptr>::iterator matchedATM = std::find_if(first,

113:             last, predicate);

114:     if (matchedATM == last) {

115: 

116:         // ATM was not found, so add the given ATM to the end of

117:         // the list.

118:         cout << "BankServerImpl: Registering ATM /"" << atm->

119:                 name() << "/"." << endl;

120:         myATMs.push_back(ATM::_duplicate(atm));

121:         return;

122:     } else {

123: 

124:         // The ATM was already registered, so throw an exception.

125:         throw InvalidATMException();

126:   }

127: }

128: 

129: void BankServerImpl::unregisterATM(ATM_ptr atm) throw

130:         (InvalidBankException) {

131: 

132:     std::vector<ATM_ptr>::iterator first = myATMs.begin();

133:     std::vector<ATM_ptr>::iterator last = myATMs.end();

134:     IsATMEqual predicate(atm);

135: 

136:     std::vector<ATM_ptr>::iterator matchedATM = std::find_if(first,

137:             last, predicate);

138:     if (matchedATM == last) {

139: 

140:         // Invalid ATM; throw an exception..

141:         cout << "BankServerImpl: Attempted to unregister invalid "

142:                 "ATM." << endl;

143:         throw InvalidATMException();

144:     }

145:     cout << "BankServerImpl: Unregistering ATM /"" << atm->name()

146:             << "/"." << endl;

147: 

148:     // Delete the given ATM.

149:     myATMs.erase(matchedATM);

150:     atm->_release();

151: }

152: 

153: ATMList* BankServerImpl::getATMs() {

154: 

155:     ATMList* list = new ATMList();

156:     CORBA::Long i;

157: 

158:     list->length(myATMs.size());

159:     for (i = 0; i < myATMs.size(); i++) {

160:         (*list)[i] = ATM::_duplicate(myATMs[i]);

161:     }

162: 

163:     cout << "BankServerImpl: Returning list of " << myATMs.size()

164:             << " ATMs." << endl;

165: 

166:     return ATMList::_duplicate(list);

167: } 

As shown in Listing 8.7, no changes are necessary to BankServerMain.cpp; the BankServer application starts up in exactly the same way it did on Day 7.

Listing 8.7. BankServerMain.cpp.
 1: // BankServerMain.cpp

 2: 

 3: #include "BankServerImpl.h"

 4: #include <iostream.h>

 5: 

 6: int main(int argc, char *const *argv) {

 7: 

 8:     // Initialize the ORB and BOA.

 9:     CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

10:     CORBA::BOA_var boa = orb->BOA_init(argc, argv);

11: 

12:     // Create a BankServerImpl object.

13:     BankServerImpl bankServer("BankServer");

14: 

15:     // Notify the BOA that the BankServerImpl object is ready.

16:     boa->obj_is_ready(&bankServer);

17: 

18:     // Wait for CORBA events.

19:     cout << "BankServer ready." << endl;

20:     boa->impl_is_ready();

21: 

22:     // When this point is reached, the application is finished.

23:     return 0;

24: }

Enhancing the Bank

In Listings 8.8 and 8.9, the BankImpl class is largely unchanged, except for the addition of issueATMCard() in lines 32-33. This method accepts a request to issue an ATMCard for a given Account. issueATMCard() first checks the Account object to see whether it exists in this Bank. If it does, the ATMCard is issued; if not, an InvalidAccountException is thrown.

Listing 8.8. BankImpl.h.
 1: // BankImpl.h

 2: 

 3: #ifndef BankImpl_h

 4: #define BankImpl_h

 5: 

 6: #include <vector>

 7: 

 8: #include "../Bank_s.h"

 9: 

10: class BankImpl : public _sk_Bank {

11: 

12: public:

13: 

14:     // Constructor.

15:     //

16:     // name - This Bank's name.

17:     BankImpl(const char* name);

18: 

19:     // Destructor.

20:     ~BankImpl();

21: 

22:     // These methods are described in Bank.idl.

23:     virtual char* name();

24:     virtual void name(const char* val);

25:     virtual char* address();

26:     virtual void address(const char* val);

27:     virtual Account_ptr createAccount(Customer_ptr customer,

28:             const char* accountType, CORBA::Float openingBalance);

29:     virtual void deleteAccount(Account_ptr account) throw

30:             (InvalidAccountException);

31:     virtual AccountList* getAccounts();

32:     virtual ATMCard_ptr issueATMCard(CORBA::Short pin, Account_ptr

33:             account) throw (InvalidAccountException);

34: 

35: protected:

36: 

37:     // Return the next available account number. The result is

38:     // returned in a static buffer.

39:     char* getNextAccountNumber();

40: 

41:     // Return the current date in the form "Mmm DD YYYY". The result

42:     // is returned in a static buffer.

43:     char* getCurrentDate();

44: 

45: private:

46: 

47:     // Default constructor.

48:     BankImpl();

49: 

50:     // This Bank's name.

51:     char* myName;

52: 

53:     // This Bank's address.

54:     char* myAddress;

55: 

56:     // This Bank's Accounts.

57:     std::vector<Account_ptr> myAccounts;

58: 

59:     // The next available account number.

60:     unsigned int myNextAccountNumber;

61: };

62: 

63: #endif
Listing 8.9. BankImpl.cpp.
  1: // BankImpl.cpp

  2: 

  3: #include "BankImpl.h"

  4: 

  5: #include <time.h>

  6: #include <string.h>

  7: #include <iostream.h>

  8: #include <algorithm>

  9: #include <functional>

 10: 

 11: #include "SavingsAccountImpl.h"

 12: #include "CheckingAccountImpl.h"

 13: #include "ATMCardImpl.h"

 14: 

 15: extern CORBA::BOA_var boa;

 16: 

 17: // STL-derived unary function which returns TRUE if Accounts are

 18: // equal.

 19: class IsAccountEqual : public std::unary_function<Account_ptr,

 20:         bool> {

 21: public:

 22:     IsAccountEqual(argument_type account) { myAccount = account; }

 23:     result_type operator()(argument_type account) { return account->

 24:             _is_equivalent(myAccount) != 0; }

 25: private:

 26:     argument_type myAccount;

 27: };

 28: 

 29: // Constructor.

 30: //

 31: // name - This Bank's name.

 32: BankImpl::BankImpl(const char* name) : _sk_Bank(name), myAccounts(),

 33:         myName(strdup(name)), myAddress(strdup("123 Elm Street, "

 34:         "Anyware USA 12345")), myNextAccountNumber(0) {

 35: 

 36: }

 37: 

 38: // Default constructor.

 39: BankImpl::BankImpl() : myAccounts(), myName(NULL), myAddress(NULL),

 40:         myNextAccountNumber(0) {

 41: 

 42: }

 43: 

 44: // Destructor.

 45: BankImpl::~BankImpl() {

 46: 

 47:     cout << "Bank /"" << name() << "/" being destroyed." << endl;

 48:     free(myName);

 49:     free(myAddress);

 50: }

 51: 

 52: char* BankImpl::name() {

 53: 

 54:     return CORBA::strdup(myName);

 55: }

 56: 

 57: void BankImpl::name(const char* val) {

 58: 

 59:     free(myName);

 60:     myName = strdup(val);

 61: }

 62: 

 63: char* BankImpl::address() {

 64: 

 65:     return CORBA::strdup(myAddress);

 66: }

 67: 

 68: void BankImpl::address(const char* val) {

 69: 

 70:     free(myAddress);

 71:     myAddress = strdup(val);

 72: }

 73: 

 74: Account_ptr BankImpl::createAccount(Customer_ptr customer,

 75:         const char* accountType, CORBA::Float openingBalance) {

 76: 

 77:     Account_ptr newAccount;

 78: 

 79:     if (strcmp(accountType, "savings") == 0) {

 80: 

 81:         // Create a new SavingsAccountImpl object for the Account.

 82:         cout << "BankImpl: Creating new SavingsAccount for "

 83:                 "Customer " << customer->name() << "." << endl;

 84:         newAccount = new SavingsAccountImpl(getNextAccountNumber(),

 85:                 getCurrentDate(), openingBalance, customer, 10.0);

 86:     } else if (strcmp(accountType, "checking") == 0) {

 87: 

 88:         // Create a new CheckingAccountImpl object for the Account.

 89:         cout << "BankImpl: Creating new CheckingAccount for "

 90:                 "Customer " << customer->name() << "." << endl;

 91:         newAccount = new CheckingAccountImpl(getNextAccountNumber(),

 92:                 getCurrentDate(), openingBalance, customer);

 93:     } else {

 94: 

 95:         // Invalid Account type; do nothing.

 96:         cout << "BankImpl: Customer " << customer->name() <<

 97:                 " requested invalid Account type /"" << accountType

 98:                 << "/"." << endl;

 99:         return Account::_nil();

100:     }

101: 

102:     // Add the created Account at the end of the list and return it.

103:     ::boa->obj_is_ready(newAccount);

104:     myAccounts.push_back(Account::_duplicate(newAccount));

105:     return newAccount;

106: }

107: 

108: void BankImpl::deleteAccount(Account_ptr account) throw

109:         (InvalidAccountException) {

110: 

111:     std::vector<Account_ptr>::iterator first = myAccounts.begin();

112:     std::vector<Account_ptr>::iterator last = myAccounts.end();

113:     IsAccountEqual predicate(account);

114: 

115:     std::vector<Account_ptr>::iterator matchedAccount = std::

116:             find_if(first, last, predicate);

117:     if (matchedAccount == last) {

118: 

119:         // Invalid Account; throw an exception.

120:         cout << "BankImpl: Attempted to delete invalid Account." <<

121:                 endl;

122:         throw InvalidAccountException();

123:     }

124:     cout << "BankImpl: Deleting Account /"" << account->

125:             accountNumber() << "/"." << endl;

126: 

127:     // Delete the given Account.

128:     myAccounts.erase(matchedAccount);

129:     account->_release();

130: }

131: 

132: AccountList* BankImpl::getAccounts() {

133: 

134:     AccountList* list = new AccountList(myAccounts.size());

135:     CORBA::Long i;

136: 

137:     for (i = 0; i < myAccounts.size(); i++) {

138:         (*list)[i] = Account::_duplicate(myAccounts[i]);

139:     }

140: 

141:     return list;

142: }

143: 

144: ATMCard_ptr BankImpl::issueATMCard(CORBA::Short pin, Account_ptr

145:         account) throw (InvalidAccountException) {

146: 

147:     // First check to see if the Account is with this Bank.

148:     std::vector<Account_ptr>::iterator first = myAccounts.begin();

149:     std::vector<Account_ptr>::iterator last = myAccounts.end();

150:     IsAccountEqual predicate(account);

151: 

152:     std::vector<Account_ptr>::iterator matchedAccount = std::

153:             find_if(first, last, predicate);

154:     if (matchedAccount == last) {

155: 

156:         // Invalid Account; throw an exception.

157:         throw InvalidAccountException();

158:     }

159: 

160:     // If we got this far, the Account must exist with this Bank,

161:     // so we can proceed.

162:     ATMCard_ptr newCard = new ATMCardImpl(pin, account);

163: 

164:     return ATMCard::_duplicate(newCard);

165: }

166: 

167: // Return the next available account number. The result is returned

168: // in a static buffer.

169: char* BankImpl::getNextAccountNumber() {

170: 

171:     static char accountNumber[16] = "Account        ";

172: 

173:     sprintf(accountNumber + 7, "%08u", myNextAccountNumber++);

174: 

175:     return accountNumber;

176: }

177: 

178: // Return the current date in the form "Mmm DD YYYY". The result is

179: // returned in a static buffer.

180: char* BankImpl::getCurrentDate() {

181: 

182:     static char currentDate[12] = "           ";

183: 

184:     time_t ltime;

185:     time(&ltime);

186:     char* ctimeResult = ctime(&ltime);

187: 

188:     memcpy(currentDate, ctimeResult + 4, 3);

189:     memcpy(currentDate + 4, ctimeResult + 8, 2);

190:     memcpy(currentDate + 7, ctimeResult + 20, 4);

191: 

192:     return currentDate;

193: } 

The logic of ATMCardImpl (Listings 8.10 and 8.11) is very similar to that in BankImpl; addAccount(), removeAccount(), and getAccounts() are implemented in exactly the same way as they are in BankImpl (with the exception that removeAccount() and addAccount() call isAuthorized() to determine whether the Account is in the list of Accounts, rather than duplicate this functionality).

Listing 8.10. ATMCardImpl.h.
 1: // ATMCardImpl.h

 2: 

 3: #ifndef ATMCardImpl_h

 4: #define ATMCardImpl_h

 5: 

 6: #include <vector>

 7: 

 8: #include "../ATMCard_s.h"

 9: 

10: class ATMCardImpl : public _sk_ATMCard {

11: 

12: public:

13:     // Constuctor.

14:     //

15:     // pin - the initial PIN to use for this ATMCard.

16:     // initialAccount - the Account for which this ATMCard is

17:     // initially authorized.

18:     ATMCardImpl(CORBA::Short pin, Account_ptr initialAccount);

19: 

20:     // Destructor.

21:     ~ATMCardImpl();

22: 

23:     // These methods are described in ATMCard.idl.

24:     virtual CORBA::Boolean isAuthorized(Account_ptr account);

25:     virtual CORBA::Short pin();

26:     virtual void pin(CORBA::Short val);

27:     virtual void removeAccount(Account_ptr account) throw

28:             (InvalidAccountException);

29:     virtual void addAccount(Account_ptr account) throw

30:             (InvalidAccountException);

31:     virtual AccountList* getAccounts();

32: 

33: private:

34: 

35:     // Default constructor.

36:     ATMCardImpl();

37: 

38:     // This ATMCard's PIN.

39:     CORBA::Short myPIN;

40: 

41:     // A list of Accounts on which this ATM is authorized.

42:     std::vector<Account_ptr> myAccounts;

43: };

44: 

45: #endif
Listing 8.11. ATMCardImpl.cpp.
  1: // ATMCardImpl.cpp

  2: 

  3: #include "ATMCardImpl.h"

  4: 

  5: #include <iostream.h>

  6: #include <algorithm>

  7: #include <functional>

  8: 

  9: // STL-derived unary function which returns TRUE if Accounts are

 10: // equal.

 11: class IsAccountEqual : public std::unary_function<Account_ptr,

 12:         bool> {

 13: public:

 14:     IsAccountEqual(argument_type account) { myAccount = account; }

 15:     result_type operator()(argument_type account) { return account->

 16:             _is_equivalent(myAccount) != 0; }

 17: private:

 18:     argument_type myAccount;

 19: };

 20: 

 21: // Constuctor.

 22: //

 23: // pin - the initial PIN to use for this ATMCard.

 24: // initialAccount - the Account for which this ATMCard is

 25: // initially authorized.

 26: ATMCardImpl::ATMCardImpl(CORBA::Short pin, Account_ptr

 27:         initialAccount) : myPIN(pin), myAccounts() {

 28: 

 29:     // Add the Account to the authorized Account list.

 30:     myAccounts.push_back(Account::_duplicate(initialAccount));

 31: }

 32: 

 33: // Default constructor.

 34: ATMCardImpl::ATMCardImpl() : myPIN(0), myAccounts() {

 35: 

 36: }

 37: 

 38: // Destructor.

 39: ATMCardImpl::~ATMCardImpl() {

 40: 

 41: }

 42: 

 43: CORBA::Short ATMCardImpl::pin() {

 44: 

 45:     return myPIN;

 46: }

 47: 

 48: void ATMCardImpl::pin(CORBA::Short val) {

 49: 

 50:     myPIN = val;

 51: }

 52: 

 53: CORBA::Boolean ATMCardImpl::isAuthorized(Account_ptr account) {

 54: 

 55:     std::vector<Account_ptr>::iterator first = myAccounts.begin();

 56:     std::vector<Account_ptr>::iterator last = myAccounts.end();

 57:     IsAccountEqual predicate(account);

 58: 

 59:     std::vector<Account_ptr>::iterator matchedAccount = std::

 60:             find_if(first, last, predicate);

 61:     if (matchedAccount == last) {

 62: 

 63:         // Account not found; return false.

 64:         return 0;

 65:     } else {

 66: 

 67:         // Account found; return true.

 68:         return 1;

 69:     }

 70: }

 71: 

 72: void ATMCardImpl::removeAccount(Account_ptr account) throw

 73:         (InvalidAccountException) {

 74: 

 75:     if (!isAuthorized(account)) {

 76: 

 77:         // Invalid Account; throw an exception.

 78:         throw InvalidAccountException();

 79:     }

 80: 

 81:     // Delete the given Account.

 82:     myAccounts.erase(&account);

 83:     account->_release();

 84: }

 85: 

 86: void ATMCardImpl::addAccount(Account_ptr account) throw

 87:         (InvalidAccountException) {

 88: 

 89:     if (isAuthorized(account)) {

 90: 

 91:         // Account has already been added, so throw an exception.

 92:         throw InvalidAccountException();

 93:     }

 94: 

 95:     // Add the created Account at the end of the list.

 96:     myAccounts.push_back(Account::_duplicate(account));

 97: }

 98: 

 99: AccountList* ATMCardImpl::getAccounts() {

100: 

101:     AccountList* list = new AccountList(myAccounts.size());

102:     CORBA::Long i;

103: 

104:     for (i = 0; i < myAccounts.size(); i++) {

105:         (*list)[i] = Account::_duplicate(myAccounts[i]);

106:     }

107: 

108:     return list;

109: } 

Because nothing different needs to be done when a BankImpl object is created, no changes are necessary from the original BankMain.cpp, but, for good measure, it appears in Listing 8.12.

Listing 8.12. BankMain.cpp.
 1: // BankMain.cpp

 2: 

 3: #include "BankImpl.h"

 4: 

 5: #include <iostream.h>

 6: 

 7: #include "../BankServer_c.h"

 8: 

 9: CORBA::BOA_var boa;

10: 

11: int main(int argc, char *const *argv) {

12: 

13:     // Check the number of arguments; there should be exactly one

14:     // (two counting the executable name itself).

15:     if (argc != 2) {

16:         cout << "Usage: Bank <bankname>" << endl;

17:         return 1;

18:     }

19: 

20:     // Assign the bank name to the first argument.

21:     const char* bankName = argv[1];

22: 

23:     // Initialize the ORB and BOA.

24:     CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

25:     ::boa = orb->BOA_init(argc, argv);

26: 

27:     // Create a Bank object.

28:     BankImpl bank(bankName);

29: 

30:     // Notify the BOA that the BankImpl object is ready.

31:     ::boa->obj_is_ready(&bank);

32: 

33:     // Locate a BankServer object and register with it.

34:     BankServer_var bankServer;

35:         try {

36:             bankServer = BankServer::_bind();

37:     } catch (const CORBA::Exception& ex) {

38: 

39:         // The bind attempt failed...

40:         cout << "BankImpl: Unable to bind to a BankServer." << endl;

41:         cout << ex << endl;

42:         return 1;

43:     }

44:     try {

45:         bankServer->registerBank(&bank);

46:     } catch (const CORBA::Exception& ex) {

47: 

48:         // The registerBank() attempt failed...

49:         cout << "BankImpl: Unable to register Bank." << endl;

50:         cout << ex << endl;

51:         return 1;

52:     }

53: 

54:     // Wait for CORBA events.

55:     cout << "Bank /"" << bankName << "/" ready." << endl;

56:     ::boa->impl_is_ready();

57: 

58:     // When this point is reached, the application is finished.

59:     return 0;

60: }

Implementing the ATM Server

The implementation of ATMImpl (see Listings 8.13 and 8.14) is clear-cut. The ATMImpl simply forwards requests to withdraw funds into an Account, to deposit funds into an Account, and to get the current balance of an Account. Each operation uses the ATMCard to authorize the transaction, and if the ATMCard approves, the transaction is forwarded to the Account. Because the transaction itself is performed by the Account, the implementations for withdraw(), deposit(), and getBalance() are simple.

Listing 8.13. ATMImpl.h.
 1: // ATMImpl.h

 2: 

 3: #ifndef ATMImpl_h

 4: #define ATMImpl_h

 5: 

 6: #include "../ATM_s.h"

 7: 

 8: class ATMImpl : public _sk_ATM {

 9: 

10: public:

11:     // Constuctor.

12:     //

13:     // name - the name of this ATM.

14:     ATMImpl(const char* name);

15: 

16:     // Destructor.

17:     ~ATMImpl();

18: 

19:     // These methods are described in ATM.idl.

20:     virtual char* name();

21:     virtual void name(const char* val);

22:     virtual CORBA::Float withdraw(ATMCard_ptr card, Account_ptr

23:             account, CORBA::Short pin, CORBA::Float amount) throw

24:             (AuthorizationException, InvalidAmountException,

25:             InsufficientFundsException);

26:     virtual CORBA::Float deposit(ATMCard_ptr card, Account_ptr

27:             account, CORBA::Short pin, CORBA::Float amount) throw

28:             (AuthorizationException, InvalidAmountException);

29:     virtual CORBA::Float getBalance(ATMCard_ptr card, Account_ptr

30:             account, CORBA::Short pin) throw

31:             (AuthorizationException);

32: 

33: private:

34: 

35:     // Default constuctor.

36:     ATMImpl();

37: 

38:     // This ATM's name.

39:     char* myName;

40: };

41: 

42: #endif
Listing 8.14. ATMImpl.cpp.
 1: // ATMImpl.cpp

 2: 

 3: #include "ATMImpl.h"

 4: 

 5: #include <iostream.h>

 6: 

 7: // Constuctor.

 8: //

 9: // name - the name of this ATM.

10: ATMImpl::ATMImpl(const char* name) : _sk_ATM(name),

11:         myName(strdup(name)) {

12: 

13: }

14: 

15: // Default constuctor.

16: ATMImpl::ATMImpl() : _sk_ATM(""), myName(NULL) {

17: 

18: }

19: 

20: // Destructor.

21: ATMImpl::~ATMImpl() {

22: 

23:     free(myName);

24: }

25: 

26: char* ATMImpl::name() {

27: 

28:     return CORBA::strdup(myName);

29: }

30: 

31: void ATMImpl::name(const char* val) {

32: 

33:     free(myName);

34:     myName = strdup(val);

35: }

36: 

37: CORBA::Float ATMImpl::withdraw(ATMCard_ptr card, Account_ptr

38:         account, CORBA::Short pin, CORBA::Float amount) throw

39:         (AuthorizationException, InvalidAmountException,

40:         InsufficientFundsException) {

41: 

42:     cout << "ATM: Authorizing Account for withdrawal." << endl;

43:     if (pin != card->pin() || !card->isAuthorized(account)) {

44: 

45:         // Incorrect PIN or card not authorized; throw an

46:         // exception.

47:         cout << "  Authorization failed." << endl;

48:         throw AuthorizationException();

49:     }

50: 

51:     cout << "  Authorization succeeded; forwarding withdrawal " <<

52:             "request to Account." << endl;

53:     return account->withdraw(amount);

54: }

55: 

56: CORBA::Float ATMImpl::deposit(ATMCard_ptr card, Account_ptr

57:         account, CORBA::Short pin, CORBA::Float amount) throw

58:         (AuthorizationException, InvalidAmountException) {

59: 

60:     cout << "ATM: Authorizing Account for deposit." << endl;

61:     if (pin != card->pin() || !card->isAuthorized(account)) {

62: 

63:         // Incorrect PIN or card not authorized; throw an

64:         // exception.

65:         cout << "  Authorization failed." << endl;

66:         throw AuthorizationException();

67:     }

68: 

69:     cout << "  Authorization succeeded; forwarding deposit " <<

70:             "request to Account." << endl;

71:     return account->deposit(amount);

72: }

73: 

74: CORBA::Float ATMImpl::getBalance(ATMCard_ptr card, Account_ptr

75:         account, CORBA::Short pin) throw (AuthorizationException) {

76: 

77:     cout << "ATM: Authorizing Account for balance." << endl;

78:     if (pin != card->pin() || !card->isAuthorized(account)) {

79: 

80:         // Incorrect PIN or card not authorized; throw an

81:         // exception.

82:         cout << "  Authorization failed." << endl;

83:         throw AuthorizationException();

84:     }

85: 

86:     cout << "  Authorization succeeded; forwarding balance " <<

87:             "request to Account." << endl;

88:     return account->balance();

89: } 

Again, the implementation in ATMMain.cpp is one that can be mostly borrowed from somewhere else. In this case, because an ATM interacts with the BankServer much the same way as a Bank does, you can borrow the code from BankMain.cpp and modify it slightly (to create and register an ATMImpl rather than a BankImpl). ATMMain.cpp appears in Listing 8.15, but compare it with BankMain.cpp (see Listing 8.12) and notice the similarity.

Listing 8.15. ATMMain.cpp.
 1: // ATMMain.cpp

 2: 

 3: #include "ATMImpl.h"

 4: 

 5: #include <iostream.h>

 6: 

 7: #include "../BankServer_c.h"

 8: 

 9: CORBA::BOA_var boa;

10: 

11: int main(int argc, char *const *argv) {

12: 

13:     // Check the number of arguments; there should be exactly one

14:     // (two counting the executable name itself).

15:     if (argc != 2) {

16:         cout << "Usage: ATM <atmname>" << endl;

17:         return 1;

18:     }

19: 

20:     // Assign the ATM name to the first argument.

21:     const char* atmName = argv[1];

22: 

23:     // Initialize the ORB and BOA.

24:     CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

25:     ::boa = orb->BOA_init(argc, argv);

26: 

27:     // Create an ATM object.

28:     ATMImpl atm(atmName);

29: 

30:     // Notify the BOA that the ATMImpl object is ready.

31:     ::boa->obj_is_ready(&atm);

32: 

33:     // Locate a BankServer object and register with it.

34:     BankServer_var bankServer;

35:     try {

36:         bankServer = BankServer::_bind();

37:     } catch (const CORBA::Exception& ex) {

38: 

39:         // The bind attempt failed...

40:         cout << "ATMImpl: Unable to bind to a BankServer." << endl;

41:         cout << ex << endl;

42:         return 1;

43:     }

44:     try {

45:         bankServer->registerATM(&atm);

46:     } catch (const CORBA::Exception& ex) {

47: 

48:         // The registerATM() attempt failed...

49:         cout << "ATMImpl: Unable to register ATM." << endl;

50:         cout << ex << endl;

51:         return 1;

52:     }

53: 

54:     // Wait for CORBA events.

55:     cout << "ATM /"" << atmName << "/" ready." << endl;

56:     ::boa->impl_is_ready();

57: 

58:     // When this point is reached, the application is finished.

59:     return 0;

60: } 

Note that in line 36 the _bind() method call reappears. Again, while this functionality appears in many ORBs, it is not standard CORBA. On Day 12 you'll replace this call to the nonstandard _bind() with the use of the CORBA Naming Service, but in the meantime, you'll continue to use _bind() for the sake of simplicity.

Implementing the ATM Client

The next step is to implement a client application that uses the newly added ATM functionality. The client can be similar in function to the BankClient application from previous chapters, except that rather than accessing an Account directly, it will do so through the ATM interface. The implementation of the ATMClient appears in Listing 8.16.

Listing 8.16. ATMClientMain.cpp.
  1: // ATMClientMain.cpp

  2: 

  3: #include <iostream.h>

  4: #include <stdlib.h>

  5: 

  6: #include "../Customer/CustomerImpl.h"

  7: 

  8: #include "../Bank_c.h"

  9: #include "../BankServer_c.h"

 10: #include "../ATM_c.h"

 11: 

 12: int main(int argc, char *const *argv) {

 13: 

 14:     // Check the number of arguments; there should be exactly five

 15:     // (six counting the executable name itself).

 16:     if (argc != 6) {

 17:         cout << "Usage: ATMClient <name> <social security number>"

 18:                 " <address> <mother's maiden name> <PIN>" << endl;

 19:         return 1;

 20:     }

 21: 

 22:     // Assign the command line arguments to the Customer attributes.

 23:     const char* name = argv[1];

 24:     const char* socialSecurityNumber = argv[2];

 25:     const char* address = argv[3];

 26:     const char* mothersMaidenName = argv[4];

 27:     CORBA::Short pin = atoi(argv[5]);

 28: 

 29:     // Initialize the ORB and BOA.

 30:     CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

 31:     CORBA::BOA_var boa = orb->BOA_init(argc, argv);

 32: 

 33:     // Create a Customer object.

 34:     cout << "ATMClient: Creating new Customer:" << endl;

 35:     cout << "  name: " << name << endl;

 36:     cout << "  Social Security number: " << socialSecurityNumber <<

 37:             endl;

 38:     cout << "  address: " << address << endl;

 39:     cout << "  mother's maiden name: " << mothersMaidenName << endl;

 40:     CustomerImpl customer(name, socialSecurityNumber, address,

 41:             mothersMaidenName);

 42: 

 43:     // Notify the BOA that the CustomerImpl object is ready.

 44:     boa->obj_is_ready(&customer);

 45: 

 46:     // Locate a BankServer object and try to get a list of Banks

 47:     // and ATMs from it.

 48:     BankServer_var bankServer;

 49:     try {

 50:         bankServer = BankServer::_bind();

 51:     } catch (const CORBA::Exception& ex) {

 52: 

 53:         // The bind attempt failed...

 54:         cout << "ATMClient: Unable to bind to a BankServer:" <<

 55:                 endl;

 56:         cout << ex << endl;

 57:         return 1;

 58:     }

 59:     cout << "ATMClient: Successfully bound to a BankServer." <<

 60:             endl;

 61: 

 62:     BankList_ptr banks;

 63:     ATMList_ptr ATMs;

 64: 

 65:     try {

 66:         banks = bankServer->getBanks();

 67:         ATMs = bankServer->getATMs();

 68:     } catch (const CORBA::Exception& ex) {

 69: 

 70:         // The attempt failed...

 71:         cout << "ATMClient: Unable to get lists of Banks and ATMs:"

 72:                 << endl;

 73:         cout << ex << endl;

 74:         return 1;

 75:     }

 76: 

 77:     // Use the first Bank and the first ATM that appear in the

 78:     // lists.

 79:     if (banks->length() == 0) {

 80: 

 81:         // No Banks were available.

 82:         cout << "ATMClient: No Banks available." << endl;

 83:         return 1;

 84:     }

 85:     if (ATMs->length() == 0) {

 86: 

 87:         // No ATMs were available.

 88:         cout << "ATMClient: No ATMs available." << endl;

 89:         return 1;

 90:     }

 91:     Bank_var bank = (*banks)[0];

 92:     ATM_var atm = (*ATMs)[0];

 93:     cout << "ATMClient: Using Bank /"" << bank->name() << "/" and"

 94:             << " ATM /"" << atm->name() << "/"." << endl;

 95: 

 96:     // Do some cool stuff.

 97: 

 98:     Account_var account;

 99:     ATMCard_var atmCard;

100: 

101:     try {

102:         account = bank->createAccount(&customer, "checking", 0.0);

103:     } catch (const CORBA::Exception& ex) {

104: 

105:         // The createAccount() attempt failed...

106:         cout << "ATMClient: Unable to create Account." << endl;

107:         cout << ex << endl;

108:         return 1;

109:     }

110: 

111:     try {

112: 

113:         // Print out some Account statistics.

114:         cout << "ATMClient: Opened new Account:" << endl;

115:         cout << "  account number: " << account->accountNumber() <<

116:                 endl;

117:         cout << "  creation date: " << account->creationDate() <<

118:                 endl;

119:         cout << "  account balance: " << account->balance() << endl;

120: 

121:         // Ask the Bank to issue an ATMCard for the newly-created

122:         // Account.

123:         cout << "ATMClient: Getting ATMCard from Bank." << endl;

124:         try {

125:             atmCard = bank->issueATMCard(pin, account);

126:         } catch (const InvalidAccountException&) {

127: 

128:             // For some reason, the Account was invalid (this

129:             // shouldn't happen).

130:             cout << "ATMClient: Exception caught: Invalid Account"

131:                     << endl;

132:             return 1;

133:         }

134: 

135:         // Perform some transactions on the Account through the

136:         // ATM.

137:         cout << "ATMClient: Performing transactions." << endl;

138:         try {

139:             cout << "  Depositing $250.00..." << endl;

140:             cout << "  New balance is ___FCKpd___20quot; << atm->deposit(atmCard,

141:                     account, pin, 250.00) << endl;

142: 

143:             // This will throw an exception since we're trying to

144:             // withdraw too much.

145:             cout << "  Withdrawing $500.00..." << endl;

146:             cout << "  New balance is ___FCKpd___20quot; << atm->withdraw(atmCard,

147:                     account, pin, 500.00) << endl;

148:         } catch (AuthorizationException&) {

149:             cout << "ATMClient: Exception caught: Invalid PIN or "

150:                     << "No authorization (as expected)" << endl;

151:         } catch (InvalidAmountException&) {

152:             cout << "ATMClient: Exception caught: Invalid amount"

153:                     << endl;

154:         } catch (InsufficientFundsException&) {

155:             cout << "ATMClient: Exception caught: Insufficient " <<

156:                     "funds" << endl;

157:         }

158: 

159:         // Perform some more transactions on the Account through

160:         // the ATM.

161:         cout << "ATMClient: Performing more transactions." << endl;

162:         try {

163:             cout << "  Depositing $500.00..." << endl;

164:             cout << "  New balance is ___FCKpd___20quot; <<

165:                     atm->deposit(atmCard, account, pin, 500.00) <<

166:                     endl;

167: 

168:             // This will throw an exception since we're using the

169:             // wrong PIN.

170:             cout << "  Withdrawing $250.00 with incorrect PIN..."

171:                     << endl;

172:             cout << "  New balance is ___FCKpd___20quot; << atm->withdraw(atmCard,

173:                     account, pin + 1, 250.00) << endl;

174:         } catch (AuthorizationException&) {

175:             cout << "ATMClient: Exception caught: Invalid PIN or "

176:                     << "No authorization (as expected)" << endl;

177:         } catch (InvalidAmountException&) {

178:             cout << "ATMClient: Exception caught: Invalid amount"

179:                     << endl;

180:         } catch (InsufficientFundsException&) {

181:             cout << "ATMClient: Exception caught: Insufficient " <<

182:                     "funds" << endl;

183:         }

184: 

185:         // Get rid of the Account.

186:         try {

187:             cout << "  Deleting Account." << endl;

188:             bank->deleteAccount(account);

189: 

190:             // Attempt to delete the Account again, just for kicks.

191:             // This should result in an exception being thrown.

192:             cout << "  Attempting to cause an exception by " <<

193:                     "deleting Account again." << endl;

194:             bank->deleteAccount(account);

195:         } catch (const InvalidAccountException&) {

196: 

197:             // Sure enough, the exception was thrown.

198:             cout << "ATMClient: Exception caught: Invalid " <<

199:                     "Account (as expected)" << endl;

200:         }

201:     } catch (const CORBA::Exception& ex) {

202: 

203:         // Some operation on the Account failed...

204:         cout << "ATMClient: Error accessing Account:" << endl;

205:         cout << ex << endl;

206:         return 1;

207:     }

208: 

209:     // When this point is reached, the application is finished.

210:     return 0;

211: } 

ATMClient.cpp is more involved than the NewCustomer.cpp application from the previous chapters (refer to Listing 8.16). It might be instructive to step through the code to see exactly what is going on:

The first three #includes (lines 3-6) will look familiar to you by now. Because the ATMClient is a client of a Bank, a BankServer, and an ATM, the header files for those client stubs are #included as well in lines 8-10.

In the first few lines of main() (lines 12-20), the ATMClient first checks the number of arguments passed on the command line. The five arguments are the Customer's name, Social Security number, address, mother's maiden name, and Personal Identification Number (PIN). If the number of arguments is not correct, the program exits.

In lines 22-31, ATMClient sets its internal parameters to the values passed on the command line and then initializes the ORB and BOA. (This section of code almost exactly duplicates the corresponding code in the NewCustomer application.)

Next, ATMClient creates a Customer object (lines 33-41) and registers it with the BOA (lines 43-44). Because there are no changes to the Customer interface from Day 7, the creation of a Customer object is exactly the same as in NewCustomer.

ATMClient's next step, in lines 46-60, is to locate a BankServer object and bind to it (in line 50). If a BankServer cannot be located, the ATMClient cannot continue and thus exits (a condition handled by the catch construct in lines 51-58).

In lines 62-75, ATMClient gets the list of available Banks and ATMs from the BankServer it previously located. Again, if there is a problem retrieving this information, the ATMClient exits.

Next, in lines 77-94, ATMClient chooses the first Bank and ATM that occur in the lists received from the BankServer. If there isn't at least one of each available (the actual checks are made in lines 79 and 85), ATMClient exits.

The next step is to open an Account with the chosen Bank, using the Customer object created previously, which the ATMClient accomplishes in lines 96-109. In lines 111-119, ATMClient prints out some statistics of the Account--account number, creation date, and current balance (which should be zero).

In lines 121-133, ATMClient requests an ATMCard from the Bank, using the pin that was passed on the command line. Because the ATMCard is being issued for the Account just opened by the same Bank, don't expect to get an InvalidAccountException here, but just as a safe practice, ATMClient tries to catch this exception anyway in line 126.

ATMClient proceeds to perform a few transactions on the Account, such as depositing and withdrawing funds, as in lines 135-157. Like the NewCustomer application, ATMClient attempts to withdraw too much from the Account (in line 146), eliciting an InsufficientFundsException (caught in line 154).

Next, in lines 159-183, ATMClient performs another transaction; this time it attempts to make a deposit into the Account, but using the wrong PIN. (It obtains an incorrect PIN by taking the PIN and adding one to it, as can be seen in line 173.) As expected, this causes an AuthorizationException to be raised (which is caught in line 174).

Finally, in lines 185-211, the ATMClient deletes the Account. Again, because the Account was created by the same Bank that ATMClient uses to delete the Account, don't expect the InvalidAccountException to be thrown, but the catch is added anyway, for good measure. Like its predecessor, ATMClient also attempts to delete the Account twice (line 194) for the purpose of demonstrating the InvalidAccountException (caught in line 195).

Running the Application

After you've successfully compiled the various application components, you're ready to run them together to see the results. As on Day 7, start by running the BankServer application; type the following:

BankServer

Again, the output of BankServer will be this:

BankServer ready.

You're now ready to start the Bank application. Actually, you can start the ATM application first because Bank and ATM objects are independent of each other (but both depend on locating a BankServer object). Start the BankServer by typing this:

BankServer "First Bank"

which in turn outputs this:

Bank "First Bank" ready.

Meanwhile, the BankServer will have output this:

BankServerImpl: Registering Bank "First Bank".

Then you'll want to start the ATM application:

ATM "First Bank ATM"

The ATM application displays the following:

ATM "First Bank ATM" ready.

The BankServer also pipes in with this message:

BankServerImpl: Registering ATM "First Bank ATM".

Finally you're ready to run the ATMClient application. You can do so by typing

ATMClient "Jeremy Rosenberger" 123456789 "123 Main Street" Doe 1234

The ATMClient will then do its magic, displaying the following:

ATMClient: Creating new Customer:

  name: Jeremy Rosenberger

  Social Security number: 123456789

  address: 123 Main Street

  mother's maiden name: Doe

ATMClient: Successfully bound to a BankServer.

ATMClient: Using Bank "First Bank" and ATM "First Bank ATM".

ATMClient: Opened new Account:

  account number: Account00000000

  creation date: Oct 20 1997

  account balance: 0

ATMClient: Getting ATMCard from Bank.

ATMClient: Performing transactions.

  Depositing $250.00...

  New balance is $250

  Withdrawing $500.00...

ATMClient: Exception caught: Insufficient funds

ATMClient: Performing more transactions.

  Depositing $500.00...

  New balance is $750

  Withdrawing $250.00 with incorrect PIN...

ATMClient: Exception caught: Invalid PIN or No authorization (as expected)

  Deleting Account.

  Attempting to cause an exception by deleting Account again.

ATMClient: Exception caught: Invalid Account (as expected)

All this will go by very quickly, but after it's all over, you'll be able to go to the other application windows and see some evidence of what transpired here. Looking first at the BankServer application, you'll see this (with new output messages highlighted in bold):

BankServer ready.

BankServerImpl: Returning list of 1 Banks.

BankServerImpl: Returning list of 1 ATMs.

Recalling the implementation of BankServerImpl, you will notice that these messages are displayed when the getBanks() and getATMs() methods are called.

Turning your attention now to the Bank application, you'll see the following messages:

Bank "First Bank" ready.

BankImpl: Creating new CheckingAccount for Customer Jeremy Rosenberger.

AccountImpl: Insufficient funds to withdraw specified amount.

BankImpl: Deleting Account "Account00000000".

BankImpl: Attempted to delete invalid Account.

Examining the output of the Bank application, you can trace the actions of the ATMClient application: A new CheckingAccount is created; at some point later, an unsuccessful attempt is made to withdraw funds from the Account, and later the Account is deleted. Recall also that the ATMClient, like the NewCustomer application before it, attempts to delete the Account twice, and a message to this effect is also displayed by the Bank application.

You will recall that the ATMClient also attempted to perform a transaction with the incorrect PIN. Notice that there is no such message to that effect here; indeed, the Bank never even saw the transaction attempt because the ATM blocked the transaction from occurring when the Customer could not be authorized by the ATMCard.

Finally, turn your attention to the output of the ATM application:

ATM "First Bank ATM" ready.

ATM: Authorizing Account for deposit.

  Authorization succeeded; forwarding deposit request to Account.

ATM: Authorizing Account for withdrawal.

  Authorization succeeded; forwarding withdrawal request to Account.

ATM: Authorizing Account for deposit.

  Authorization succeeded; forwarding deposit request to Account.

ATM: Authorizing Account for withdrawal.

  Authorization failed.

The output from the ATM provides the most insight into what the ATMClient is doing. Each operation, whether successful or not, is logged by the ATM application. As you see, each transaction is successful except the last, in which the ATMClient deliberately attempts to use the wrong PIN for the transaction.

Summary

In this chapter, you added a fair amount of functionality to the Bank application. Most of it was added in the form of a new system component, the ATM application, and its counterpart, the ATMCard. You also modified the other components of the system to work with the ATM: Notably, the BankServer was modified to enable ATMs to register, the Bank was modified to issue ATMCards on request, and, of course, the NewCustomer application was revised to use an ATM and ATMCard for all its transactions. This chapter gives you the opportunity to work with a more complex CORBA application, involving a number of components that interact with each other.

On Day 9, you'll implement the final installment of the Bank application. So far, the client portion of the application (NewCustomer yesterday, ATMClient today) controls most of the process. In other words, the server components spend their time waiting for a transaction to be initiated by the client component. This is commonly referred to as a pull model because the client "pulls" information from the servers as it is needed.

On Day 9, you'll add a new capability: Through callbacks, the server components can "push" messages to the client. In other words, if a server (for instance, a Bank) wants to send a message to the Customer, it won't have to wait for the Customer to pull that information from the Bank; it can push the information to the Customer at any time. This is useful if the Customer subscribes through the Bank to a service that, for example, periodically provides stock updates.

Q&A

Q It seems to me that the ATM interface is little more than a pass-through for Customers to access their Accounts.

A
If you noticed this property of the ATM interface, give yourself a pat on the back. The ATM doesn't actually introduce any new functionality to the application--at least where Account operations are concerned--but simply provides another way to access Accounts.

Q If this application were to actually be deployed, wouldn't security be a major issue? For instance, it looks like it would be easy to guess a customer's PIN through brute force.

A
If you're asking this question, you've no doubt observed the complete lack of a security mechanism in the application (other than the PIN mechanism itself). Of course, in a production application, you'd need to make additional security considerations to prevent password snooping, brute force attacks, and so on. (You might want to use the CORBA Security Service or your own mechanisms to implement and enforce security policies.)

Workshop

The following section will help you test your comprehension of the material presented in this chapter and put what you've learned into practice. You'll find the answers to the quiz and exercise in Appendix A.

Quiz

What are the four steps you'll typically follow to make enhancements to a CORBA application?

Exercise

Add an operation to the ATM interface that allows funds to be transferred between Accounts. Be sure to provide appropriate exceptions as well.

 

Previous chapter Next chapter Contents


Macmillan Computer Publishing USA
 
© Copyright, Macmillan Computer Publishing. All rights reserved.
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值