VisiTransact Guide : A quick start with VisiTransact

A quick start with VisiTransact
This section describes the development of distributed, object-based transactional applications with VisiTransact using a C++ sample application.
For a Java example, refer to the <install_dir>\examples\vbroker\its folder.
Overview of the example
This C++ example involves a bank that has several accounts. During a transaction, money is transferred between at least two of these accounts—depending upon the parameters passed to the client program.
The programs for the quick start example are:
transfer. This program takes input from the command line about how much money should be transferred between which accounts. It then begins the transaction and performs the requested transfer. After all requested transfers have completed, it requests to complete the transaction (either commit or rollback).
bank_server. This program binds to a Storage object and creates a Bank object with the name entered at the command line.
storage_server. This program implements a Storage object for the non-database quick start, ensures that changes made during the transaction to balances are stored persistently (if committed), or that the account balances are returned to their state before the transaction (if rolled back).
The objects for the quick start example are:
Bank. This object provides access to existing Account objects. It creates instances of the Account object for accounts that exist in the Storage object.
Account. This object lets you view the balance for an account, and credit or debit an account's balance. It uses the Storage object to interact with persistent data.
Storage. The purpose of this object is to abstract data access into one object that makes changes to data on behalf of the accounts.
StorageServerImpl. This implementation of the Storage object contains a lightweight Resource (FakeResourceImpl) that simply updates balances in memory. It is only provided to help you get up and running quickly with VisiTransact.
Figure 6
Files for the quick start example
If you do not know the location of the VisiTransact package, see your system administrator. The following table lists the files included for the example.
The server program that creates the Bank object with information from the storage_server or storage_ora program, and makes it available to client programs.
Note
To aid in portability, the example files use the .C extension on both Windows and UNIX so that there can be a common Makefile.
Prerequisites for running the example
You must install the VisiTransact product, and the VisiBroker C++ Developer (ORB). You must also start an instance of the VisiTransact Transaction Service, as described in “Running the example”.
What you will do in this example
Here are the steps to implement the C++ quick start:
1
2
3
Implement the bank_server program. Initialize the ORB, create a Bank and access a Storage object, register the Bank object with the POA, and prepare to receive requests. See “Writing the bank_server program”.
4
5
6
7
8
Writing the quick start IDL
The first step to creating a transactional application with VisiTransact is to specify all of your interfaces using the CORBA Interface Definition language (IDL). IDL is language-independent and has a syntax similar to C++, but can be mapped to a variety of programming languages.
The example below shows the contents of the quickstart.idl file which defines the three objects required for the quick start example—Bank, Account, and Storage.
// quickstart.idl
#include "CosTransactions.idl"
#pragma prefix "visigenic.com"

module quickstart
{
//requires
interface Account
{
float balance();
void credit(in float amount);
void debit(in float amount);
};

exception NoSuchAccount
{
string account_name;
};

interface Bank
{
Account get_account(in string account_name)
raises(NoSuchAccount);
};

typedef sequence<string> AccountNames;

//adapts
interface Storage
{
float balance(in string account)
raises(NoSuchAccount);
void credit(in string account, in float amount)
raises(NoSuchAccount);
void debit(in string account, in float amount)
raises(NoSuchAccount);
AccountNames account_names();
};

};
The interface specification you create in IDL is used by the VisiBroker ORB's idl2cpp compiler to generate C++ stub routines for the client application, and skeleton code for the objects. The stub routines are used by the client program for all method invocations. You use the skeleton code, along with code you write, to create the server programs that implement the objects.
The code for the client and servers, once completed, is used as input to your C++ compiler and linker to produce your client and server programs.
Writing the transaction originator (transfer client program)
The file named transfer.C contains the implementation of the transaction originator, which also happens to be the client program. As discussed in “Overview of transaction processing” the transaction originator is not always the client program. The transfer client program performs a single VisiTransact-managed transaction (see “Creating and propagating VisiTransact-managed transactions” for details). For information on how to manage transactions in other ways, see “Other methods of creating and propagating transactions”.
The client program performs these steps:
1
2
3
4
5
Invokes the debit() and credit() methods on the Account objects for each set of source/destination/amount entries to the transfer client program. It prints out the current balances for each Account before and after the transfer.
6
7
Initializing the ORB
The first task that your transaction originator needs to do is initialize the ORB, as shown in the example below. As a component of VisiBroker, command-line arguments for VisiTransact are supplied to VisiTransact through the VisiBroker ORB initialization call ORB_init(). Therefore, in order for arguments specified on the command line to have effect on the VisiTransact operation in a given application process, applications must pass the original argc and argv arguments to ORB_init() from the main program.
...
int main(int argc, char* const* argv)
{
try
{
// Initialize the ORB.
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
...
The ORB_init() function will parse both ORB arguments and VisiTransact arguments, removing them from the argv vector before returning.
Binding to the Bank object
Before the transfer client program can invoke methods on the transactional (Account) objects, it must first use the _bind() method to establish a connection to the Bank object. The implementation of the _bind() method is generated automatically by the idl2cpp compiler. The _bind() method requests the ORB to locate and establish a connection to the Bank object.
The following example shows how to bind to the Bank object as specified in the bank_name parameter passed at the command line when the transfer client program is started. Notice how a _var is used to facilitate memory management.
const char *bank_name = argv[1];
//Locate the bank.
Quickstart::Bank_var bank;
//Get the Bank ID
PortableServer::ObjectId_var bankId =
PortableServer::string_to_ObjectId(bank_name);
try
{
bank = quickstart::Bank::_bind("/bank_agent_poa", bankId);
//bank = quickstart::Bank::_bind(bank_name);
}
catch (CORBA::Exception &ex)
{
const char *name;
(bank_name == 0) ? name="NULL" : name=bank_name;
cerr << "Unable to bind to Bank \"" << name << "\": " << ex << endl;
return 1;
}
Beginning the transaction
Before beginning a transaction, you must obtain a transaction context. VisiTransact-managed transactions are handled transparently to your application with Current—an object which maintains a unique transaction for each active thread. To use a VisiTransact-managed transaction, you must obtain a reference to this Current object. The Current object is valid for the entire process under which you create it, and can be used in any thread.
The next example shows how to obtain a VisiTransact-managed transaction. First an object reference is obtained for the TransactionCurrent object using the CORBA::ORB::resolve_initial_references() method. The Current object returned from this method is then narrowed to the specific CosTransactions::Current object using the narrow() method. See the VisiBroker documentation for a full description of the resolve_initial_references()and narrow() methods.
// Start a transaction.
CosTransactions::Current_var current;
{
CORBA::Object_var initRef =
orb->resolve_initial_references("TransactionCurrent");
current = CosTransactions::Current::_narrow(initRef);
}
...
To perform work that is managed by VisiTransact, you must first begin a transaction using the Current interface's begin() method. Only one transaction can be active within a thread at a time. The following example shows how to begin a VisiTransact-managed transaction.
...
CosTransactions::Current_var current;
...
current->begin();
...
Obtaining references to transactional objects (source and destination accounts)
Once you bind to the Bank object, you can obtain a reference to the transactional (Account) objects specified when the transfer program is started. Within the transfer program, these references are obtained using the get_account() method in the Bank interface. The example below shows the relevant code from the transfer program.
...
try
{
for(CORBA::ULong i = 2; i < (CORBA::ULong)argc; i += 3)
{
const char* srcName = argv[i];
const char* dstName = argv[i + 1];
float amount = (float)atof(argv[i + 2]);

quickstart::Account_var src = bank->get_account(srcName);
quickstart::Account_var dst = bank->get_account(dstName);
...
}
}
catch(const quickstart::NoSuchAccount& e)
{
cout << "Exception: " << e << endl;
commit = 0;
}
catch(const CORBA::SystemException& e)
{
cout << "Exception: " << e << endl;
commit = 0;
}
...
In the above example, the transfer client program loops through its input arguments (received at the command line when the program started), and calls get_account() for each source and destination account name entered. If the account name entered is valid, the Bank object returns a corresponding Account object. See “Implementing the Bank object and its get_account() method” for details on the Bank object's get_account() method.
Notice that if an invalid account name was entered, an error message is printed and the value of the commit variable is set to false. Likewise, if a system exception is raised when performing the invocation of get_account(), an error message is printed and the value of the commit variable is set to false. See “Committing or rolling back the transaction” to find out how the commit variable is used for transaction completion.
Invoking methods (debit() and credit()) on the transactional (account) objects
Once the transfer client program has established a connection with the source and destination Account objects, the debit() and credit() methods of the Account interface can be invoked for each source/destination/amount triplet that was entered when the transfer program was started.
The debit() and credit() methods are invoked from within the transfer program's main try() clause using the information returned to the src and dst variables by the invocation of the get_account() method shown in the previous example. The next example shows the parts of the try() clause that invoke credit() and debit().
try
{
for(CORBA::ULong i = 2; i < (CORBA::ULong)argc; i += 3)
{
...
src->debit(amount);
dst->credit(amount);
...
}
}
...
Committing or rolling back the transaction
Once a transaction has begun, it must be committed or rolled back to complete the transaction. If an originator of a VisiTransact-managed transaction does not complete the transaction, the VisiTransact Transaction Service will rollback the transaction after a timeout period. However, it is important to commit or rollback transactions so that hung transactions do not consume system resources.
The example below shows how the transfer program uses the commit variable to decide whether to commit or rollback the transaction. If the commit variable is 1 (true), the transaction is committed. If the commit variable is 0 (false), the transaction is rolled back. In the next example, the 0 sent to commit() means that heuristics will not be reported. See “Transaction completion” for information about heuristics.
...
CORBA::Boolean commit = 1;
...
if(commit)
{
cout << "*** Committing transaction ***" << endl;
current->commit(0);
}
else
{
cout << "*** Rolling back transaction ***" << endl;
current->rollback();
}
...
Handling exceptions
The following example shows the outer try and catch statements for the transfer client program. Notice how these statements are used to detect any failures (CORBA or application exceptions), print a message, and return.
try
{
...
}
catch(const CORBA::Exception& e)
{
cerr << "Exception: " << e << endl;
return 1;
}
catch(...)
{
cerr << "Unknown Exception caught" << endl;
return 1;
}
return 0;
...
Writing the bank_server program
The bank_server program performs these steps in the main routine:
1
2
3
4
The argc and argv parameters passed to the ORB_init() methods are the same parameters that are passed to the main routine. These parameters can be used to specify options for the ORB.
int main(int argc, char* const* argv)
{
try
{
// Initialize the ORB.
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
...
Next, the myPOA that is to be used to activate the Storage object is created. The bank_server program then obtains a Storage object, and retrieves account information from it. Using the account information, the bank_server program instantiates the Bank object. Lastly, the bank_server program calls the orb->run() method to start the event loop that receiveclient requests.
const char* bank_name = argv[1];
// get a reference to the root POA
CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");
PortableServer::POA_var rootPOA = PortableServer::POA::_narrow(obj);

CORBA::PolicyList policies;
policies.length(1);
policies[(CORBA::ULong)0] = rootPOA->create_lifespan_policy(
PortableServer::PERSISTENT);
// get the POA Manager
PortableServer::POAManager_var poa_manager = rootPOA->the_POAManager();

// Create myPOA with the right policies
PortableServer::POA_var myPOA = rootPOA->create_POA("bank_agent_poa",
poa_manager, policies);
// Get the Bank Id
PortableServer::ObjectId_var bankId =
PortableServer::string_to_ObjectId(bank_name);

// Get a storage object for the bank.
quickstart::Storage_var storage = quickstart::Storage::_bind("/
bank_storage_poa", bankId);

// Create the bank servant
PortableServer::ServantBase_var bankServant = new BankImpl(bank_name, storage, orb);

// Decide on the ID for the servant
PortableServer::ObjectId_var managerId =
PortableServer::string_to_ObjectId(bank_name);

// Activate the servant with the ID on myPOA
myPOA->activate_object_with_id(managerId, bankServant);

// Activate the POA Manager
poa_manager->activate();

CORBA::Object_var reference = myPOA->servant_to_reference(bankServant);
cout << reference << " is ready" << endl;

// Wait for incoming requests
orb->run();
Writing the Bank object
There are a few tasks you must do to implement the Bank object:
Derive the BankImpl class from the POA_quickstart::Bank skeleton class.
Understanding the BankImpl class hierarchy
The BankImpl class that you implement is derived from the POA_quickstart::Bank class that was generated by the idl2cpp compiler. The following example shows the BankImpl class.
class BankImpl : public POA_quickstart::Bank
{
private:
quickstart::AccountNames_var _account_names;
quickstart::Storage_var _storage;
AccountRegistry _accounts;
PortableServer::POA_var _account_poa;
public:
BankImpl(const char* bank_name,
quickstart::Storage* storage, CORBA::ORB* orb);
virtual ~BankImpl();
virtual quickstart::Account* get_account(const char* account_name);
};
Implementing the Bank object and its get_account() method
The BankImpl interface defines its constructor and destructor. The constructor creates a Bank object with the name provided when the bank_server program is started (bank_name). It also creates an instance of AccountRegistry which is used to keep trace of all instantiated Account objects. The account names are obtained from the Storage object.
BankImpl::BankImpl(const char* bank_name,
quickstart::Storage* storage, CORBA::ORB* orb)
{
_account_names = storage->account_names();
_storage = quickstart::Storage::_duplicate(storage);

PortableServer::POA_var root_poa =
PortableServer::POA::_narrow(orb->resolve_initial_references("RootPOA"));
CORBA::PolicyList policies;
policies.length(2);
CORBA::Any policy_value;
policy_value <<= CosTransactions::REQUIRES;
policies[0] = orb->create_policy(CosTransactions::OTS_POLICY_TYPE,
policy_value);
policies[1] =
root_poa->create_implicit_activation_policy(PortableServer::IMPLICIT_ACTIVATION);
_account_poa = root_poa->create_POA("account_poa",
PortableServer::POAManager::_nil(), policies);
_account_poa->the_POAManager()->activate();
return;
}

BankImpl::~BankImpl()
{
}
The next example shows the Bank object's get_account() method. Note that the get_account() method performs a check to see if the account exists, else it will create a new account. If it does not, a NoSuchAccount exception is thrown.
quickstart::Account_ptr
BankImpl::get_account(const char* account_name)
{
// Lookup the account in the account dictionary.
PortableServer::ServantBase_var servant = _accounts.get(account_name);
CORBA::Boolean foundAccount = 0;

if (servant == PortableServer::ServantBase::_nil()) {
for(CORBA::ULong i = 0; !foundAccount && i < _account_names->length(); i++) {
if (!strcmp(_account_names[i], account_name)) {
servant = new AccountImpl(account_name, _storage);

// Print out the new account
cout << "Created " << account_name << "'s account." << endl;

// Save the account in the account dictionary.
_accounts.put(account_name, servant);
foundAccount = 1;
}
}
if (!foundAccount) {
throw quickstart::NoSuchAccount(account_name);
return 0;
}
}

try {
CORBA::Object_var ref = _account_poa->servant_to_reference(servant);
quickstart::Account_var account = quickstart::Account::_narrow(ref);
cout << "account generated." << endl;
return quickstart::Account::_duplicate(account);
}
catch(const CORBA::Exception& e) {
cerr << "_narrow caught exception: " << e << endl;
return quickstart::Account::_nil();
}
throw quickstart::NoSuchAccount(account_name);
return 0;
}
Writing the transactional object (Account)
There are a few tasks you must complete to implement the transactional (Account) object:
Derive the AccountImpl class from the POA_quickstart::Account class.
Implement the Account object with implementations for the balance(), credit(), and debit() methods that invoke the Storage object.
Understanding the AccountImpl class hierarchy
The AccountImpl class that you implement is derived from the POA_quickstart::Account class that was generated by the idl2cpp compiler. Refer to the first code example in the previous section. The account_poa has a policy OTS_POLICY_TYPE of REQUIRE defined, hence all objects that are activated on this poa will need to be transactional objects.
class AccountImpl : public POA_quickstart::Account
{
private:
CORBA::String_var _account_name;
quickstart::Storage_var _storage;
public:
AccountImpl(const char* account_name,
quickstart::Storage* storage);
virtual CORBA::Float balance();
virtual void credit(CORBA::Float amount);
virtual void debit(CORBA::Float amount);
private:
virtual void markForRollback();
};
Making the Account object a transactional object
To make an object transactional two things must be done:
The _account_poa was created during the construction of the BankImpl object. Refer to the first code sample in “Implementing the Bank object and its get_account() method”. In the get_account() function, whenever a new account is needed it will be activated using the _account_poa. This makes the Account object a transactional object.
Implementing the Account object and its methods
As shown in the following example, the AccountImpl class defines its constructor which creates an Account object with the account_name and storage parameters provided by the Bank object.
AccountImpl::AccountImpl(const char* account_name,
quickstart::Storage* storage)
{
_account_name = CORBA::strdup(account_name);
_storage = quickstart::Storage::_duplicate(storage);
}
As shown in the next example, the Account class also implements a markForRollback() method. When invoked, this method calls rollback_only() to force the transaction originator to rollback the transaction.
void AccountImpl::markForRollback()
{
try
{
CORBA::ORB_var orb = CORBA::ORB_init();
CORBA::Object_var initRef =
orb->resolve_initial_references("TransactionCurrent");
CosTransactions::Current_var current =
CosTransactions::Current::_narrow(initRef);
current->rollback_only();
}
catch(const CosTransactions::NoTransaction&)
{
throw CORBA::TRANSACTION_REQUIRED();
}
}
Notice how the markForRollback() method obtains a handle to the TransactionCurrent object, and then narrows to a Current object so that it can call current->rollback_only(). Since the Account object is not the transaction originator, it cannot invoke rollback()—with VisiTransact-managed transactions, only the transaction originator can complete the transaction.
As shown in the next example, the Account object also implements the balance(), credit(), and debit() methods:
The balance() method requests the current balance for the Account object from the Storage object.
The credit() method requests that the Storage object increment the balance by the amount parameter.
The debit() method requests that the Storage object decrease the balance by the amount parameter.
Note
Although the Account object for the quick start example could easily interact with the database itself, the example is designed to mirror real-world scenarios where a back-end data access object is used by multiple business logic objects. This makes it easy to change your database in the future, if it is necessary to do so.
CORBA::Float AccountImpl::balance()
{
try
{
return _storage->balance(_account_name);
}
catch(const quickstart::NoSuchAccount& e)
{
cerr << "Account::balance: " << e << endl;
markForRollback();
return 0;
}
}

void

AccountImpl::credit(CORBA::Float amount)
{
if(amount < 0)
{
cerr << "Account::credit: Invalid amount: " << amount << endl;
markForRollback();
}
try
{
_storage->credit(_account_name, amount);
}
catch(const quickstart::NoSuchAccount& e)
{
cerr << "Account::credit: " << e << endl;
markForRollback();
}
}

void
AccountImpl::debit(CORBA::Float amount)
{
if(amount < 0 || balance() - amount < 0)
{
cerr << "Account::debit: Invalid amount: " << amount << endl;
markForRollback();
}
try
{
_storage->debit(_account_name, amount);
}
catch(const quickstart::NoSuchAccount& e)
{
cerr << "Account::debit: " << e << endl;
markForRollback();
}
}
Building the example
The transfer.C file that you created and the generated quickstart_c.C file are compiled and linked to create the client program. The bank_server.c file that you created, along with the generated quickstart_s.C, quickstart_c.C, and bank.C files, are compiled and linked to create the bank_server program. Because Current is a pseudo object and VisiTransact-managed transactions use the Current object, the client program and server programs must also be linked with the VisiTransact its_support library.
Selecting a Makefile
The <install_dir>/examples/vbe/its/ directory of your VisiTransact release contains a Makefile for this example. This directory also contains a itsmk file which is included by the Makefile and defines all site-specific settings. You may need to customize the itsmk file. The itsmk file assumes that VisiTransact has been installed in the default installation directory for VisiBroker.
Compiling the example with make
Windows
Assuming the VisiBroker ORB and VisiTransact distribution were installed in C:\vbroker, use the following commands:
prompt> C:
prompt> cd c:\vbroker\examples\vbe\its
prompt> nmake cpp
The Visual C++ nmake command, a standard facility, runs the idl2cpp compiler and then compiles each file.
UNIX
Assuming the VisiBroker ORB and VisiTransact distribution were installed in /usr/local/vbroker, issue these commands:
prompt> cd /usr/local/vbroker/examples/vbe/its
prompt> make cpp
In this example, make is the standard UNIX facility.
Running the example
Now that you have compiled the necessary components, you are ready to run your first VisiTransact application.
Starting the Smart Agent (osagent)
Before you attempt to run VisiTransact transactional applications, you must first start the VisiBroker Smart Agent on at least one host in your local network.
If the Smart Agent has not been set up as a Windows service, or on UNIX, use the following command to start the Smart Agent:
prompt> osagent
While running the example, you only need to start the Smart Agent once.
Starting the VisiTransact Transaction Service
You must start an instance of the VisiTransact Transaction Service to enable transactions across the network. To do so, use the following command:
prompt> ots
While running the example, you only need to start the VisiTransact Transaction Service once.
Starting the storage_server program
Start the storage_server program at the command line by typing:
prompt> storage_server MyBank
The argument MyBank is the name of the Bank.
Starting the bank_server program
Start the bank_server program at the command line by typing:
prompt> bank_server MyBank
In the above example, the argument is the name of the Bank.
Note
Make sure the PATH environment variable includes the path to the VisiTransact directory (where the binaries are located). On Solaris, make sure the LD_LIBRARY_PATH environment variable includes the path to the VisiTransact shared libraries.
Running the Transaction Originator (transfer Client Program)
Start the transfer program at the command line with the name of the bank, followed by the source account, destination account, and amount of money you wish to transfer.
prompt> transfer MyBank Paul John 20
You can include multiple transfers within one execution of the transfer program. To do so, include the source account, destination account, and amount in sequence for each transfer:
prompt> transfer MyBank Paul John 20 Ringo George 40
Results
Running the transfer client program with “MyBank Paul John 20” results in the following output from the transfer client program:
Account Balance
======= =======
Paul 100.0
John 100.0
*** Transfer $20.0 from Paul's account to John's account ***
Account Balance
======= =======
Paul 80.0
John 120.0
*** Committing transaction ***
 
Figure 7
Viewing the complete example
The following sections show the complete code for the quick start application.
IDL for the quick start example
// quickstart.idl
#include "CosTransactions.idl"
#pragma prefix "visigenic.com"

module quickstart
{
//requires
interface Account
{
float balance();
void credit(in float amount);
void debit(in float amount);
};

exception NoSuchAccount
{
string account_name;
};

interface Bank
{
Account get_account(in string account_name)
raises(NoSuchAccount);
};

typedef sequence<string> AccountNames;

//adapts
interface Storage
{
float balance(in string account)
raises(NoSuchAccount);
void credit(in string account, in float amount)
raises(NoSuchAccount);
void debit(in string account, in float amount)
raises(NoSuchAccount);
AccountNames account_names();
};
};
Transfer client program
This example shows the full transfer client program in the transfer.C file.
// transfer.C

#include "quickstart_c.hh"

USE_STD_NS

int
main(int argc, char* const* argv)
{
try
{
// Initialize the ORB.
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

// Check the command line arguments
if (argc % 3 != 2)
{
cerr << "Usage: " << argv[0] <<
" <bank-name> [<src> <dst> <amount>] ..." << endl;
return 1;
}

// parse first arg
const char *bank_name = argv[1];

// Locate the bank.
quickstart::Bank_var bank;

// Get the Bank Id
PortableServer::ObjectId_var bankId =
PortableServer::string_to_ObjectId(bank_name);
try
{
bank = quickstart::Bank::_bind("/bank_agent_poa", bankId);
// bank = quickstart::Bank::_bind(bank_name);
}
catch (CORBA::Exception &ex)
{
const char *name;
(bank_name == 0) ? name="NULL" : name=bank_name;
cerr << "Unable to bind to Bank \"" << name << "\": " << ex << endl;
return 1;
}

// Start a transaction.
CosTransactions::Current_var current;
{
CORBA::Object_var initRef =
orb->resolve_initial_references("TransactionCurrent");
current = CosTransactions::Current::_narrow(initRef);
}

current->begin();

CORBA::Boolean commit = 1;
try
{
for(CORBA::ULong i = 2; i < (CORBA::ULong)argc; i += 3)
{
const char* srcName = argv[i];
const char* dstName = argv[i + 1];
float amount = (float)atof(argv[i + 2]);

quickstart::Account_var src = bank->get_account(srcName);
quickstart::Account_var dst = bank->get_account(dstName);

cout << "Account\tBalance" << endl;
cout << "=======\t=======" << endl;
cout << srcName << "\t" << src->balance() << endl;
cout << dstName << "\t" << dst->balance() << endl;
cout << "\n*** Transfer $" << amount << " from " <<
srcName << "'s account to " << dstName << "'s account ***\n" << endl;

src->debit(amount);
dst->credit(amount);

cout << "Account\tBalance" << endl;
cout << "=======\t=======" << endl;
cout << srcName << "\t" << src->balance() << endl;
cout << dstName << "\t" << dst->balance() << endl;
}
}
catch(const quickstart::NoSuchAccount& e)
{
cout << e << endl;
commit = 0;
}
catch(const CORBA::SystemException& e)
{
cout << "Exception: " << e << endl;
commit = 0;
}

// Commit or rollback the transaction.
if(commit)
{
cout << "*** Committing transaction ***" << endl;
current->commit(0);
}
else
{
cout << "*** Rolling back transaction ***" << endl;
current->rollback();
}
}

catch(const CORBA::Exception& e)
{
cerr << "Exception: " << e << endl;
return 1;
}
catch(...)
{
cerr << "Unknown Exception caught" << endl;
return 1;
}
return 0;
}
bank_server program
The following example shows the bank_server program in the bank_server.C file.
// bank_server.C

#include "bank.h"

USE_STD_NS

int
main(int argc, char* const* argv)
{
try
{
// Initialize the ORB.
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

// Check the command line arguments
if(argc != 2)
{
cerr << "Usage: " << argv[0] << " <bank-name>" << endl;
return 1;
}
const char* bank_name = argv[1];

// get a reference to the root POA
CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");
PortableServer::POA_var rootPOA = PortableServer::POA::_narrow(obj);

CORBA::PolicyList policies;
policies.length(1);
policies[(CORBA::ULong)0] = rootPOA->create_lifespan_policy(
PortableServer::PERSISTENT);

// get the POA Manager
PortableServer::POAManager_var poa_manager = rootPOA->the_POAManager();

// Create myPOA with the right policies
PortableServer::POA_var myPOA = rootPOA->create_POA("bank_agent_poa",
poa_manager,
policies);
// Get the Bank Id
PortableServer::ObjectId_var bankId =
PortableServer::string_to_ObjectId(bank_name);

// Get a storage object for the bank.
quickstart::Storage_var storage = quickstart::Storage::_bind("/
bank_storage_poa", bankId);

// Create the bank servant
PortableServer::ServantBase_var bankServant = new BankImpl(bank_name,
storage, orb);

// Decide on the ID for the servant
PortableServer::ObjectId_var managerId =
PortableServer::string_to_ObjectId(bank_name);

// Activate the servant with the ID on myPOA
myPOA->activate_object_with_id(managerId, bankServant);

// Activate the POA Manager
poa_manager->activate();

CORBA::Object_var reference = myPOA->servant_to_reference(bankServant);
cout << reference << " is ready" << endl;

// Wait for incoming requests
orb->run();
}
catch(const CORBA::Exception& e)
{
cerr << "Exception: " << e << endl;
return 1;
}
catch(...)
{
cerr << "Unknown Exception caught" << endl;
return 1;
}
return 0;
}
Bank and account (transactional) objects
The example below shows the AccountRegistry, Bank, and Account classes in the bank.h file.
// bank.h

#include "quickstart_s.hh"
#include <vport.h>

// The AccountRegistry is a holder of Bank account implementations
class AccountRegistry
{
public:
AccountRegistry() : _count(0), _max(16), _data((Data*)NULL)
{
_data = new Data[16];
}
~AccountRegistry() { delete[] _data; }

void put(const char* name, PortableServer::ServantBase_ptr servant) {

VISMutex_var lock(_lock);

if (_count + 1 == _max) {
Data* oldData = _data;
_max += 16;
_data = new Data[_max];
for (CORBA::ULong i = 0; i < _count; i++)
_data[i] = oldData[i];
delete[] oldData;
}

_data[_count].name = name;
servant->_add_ref();
_data[_count].account = servant;
_count++;
}

PortableServer::ServantBase_ptr get(const char* name) {

VISMutex_var lock(_lock);

for (CORBA::ULong i = 0; i < _count; i++) {
if (strcmp(name, _data[i].name) == 0) {
_data[i].account->_add_ref();
return _data[i].account;
}
}
return PortableServer::ServantBase::_nil();
}
private:
struct Data {
CORBA::String_var name;
PortableServer::ServantBase_var account;
};

CORBA::ULong _count;
CORBA::ULong _max;
Data* _data;
VISMutex _lock; // Lock for synchronization
};

class BankImpl : public POA_quickstart::Bank
{
private:
quickstart::AccountNames_var _account_names;
quickstart::Storage_var _storage;
AccountRegistry _accounts;
PortableServer::POA_var _account_poa;
public:
BankImpl(const char* bank_name,
quickstart::Storage* storage, CORBA::ORB* orb);
virtual ~BankImpl();
virtual quickstart::Account* get_account(const char* account_name);
};

class AccountImpl : public POA_quickstart::Account
{
private:
CORBA::String_var _account_name;
quickstart::Storage_var _storage;
public:
AccountImpl(const char* account_name,
quickstart::Storage* storage);
virtual CORBA::Float balance();
virtual void credit(CORBA::Float amount);
virtual void debit(CORBA::Float amount);
private:
virtual void markForRollback();
};
The next example shows the BankImpl and AccountImpl classes in the bank.C file.
// bank.C

#include "bank.h"

USE_STD_NS

BankImpl::BankImpl(const char* bank_name,
quickstart::Storage* storage, CORBA::ORB* orb)
{
_account_names = storage->account_names();
_storage = quickstart::Storage::_duplicate(storage);

PortableServer::POA_var root_poa = PortableServer::POA::_narrow
(orb->resolve_initial_references("RootPOA"));
CORBA::PolicyList policies;
policies.length(2);
CORBA::Any policy_value;
policy_value <<= CosTransactions::REQUIRES;
policies[0] = orb->create_policy(CosTransactions::OTS_POLICY_TYPE,
policy_value);
policies[1] = root_poa->create_implicit_activation_policy
(PortableServer::IMPLICIT_ACTIVATION);
_account_poa = root_poa->create_POA("account_poa",
PortableServer::POAManager::_nil(),
policies);
_account_poa->the_POAManager()->activate();
return;
}

BankImpl::~BankImpl()
{
}

quickstart::Account_ptr
BankImpl::get_account(const char* account_name)
{
// Lookup the account in the account dictionary.
PortableServer::ServantBase_var servant = _accounts.get(account_name);
CORBA::Boolean foundAccount = 0;

if (servant == PortableServer::ServantBase::_nil()) {
 for(CORBA::ULong i = 0; !foundAccount && i < _account_names->length(); i++) {
if (!strcmp(_account_names[i], account_name)) {
servant = new AccountImpl(account_name, _storage);

// Print out the new account
cout << "Created " << account_name << "'s account." << endl;

// Save the account in the account dictionary.
_accounts.put(account_name, servant);

foundAccount = 1;
}
}
if (!foundAccount) {
throw quickstart::NoSuchAccount(account_name);
return 0;
}
}

try {
CORBA::Object_var ref = _account_poa->servant_to_reference(servant);
quickstart::Account_var account = quickstart::Account::_narrow(ref);
cout << "account generated." << endl;
return quickstart::Account::_duplicate(account);
}
catch(const CORBA::Exception& e) {
cerr << "_narrow caught exception: " << e << endl;
return quickstart::Account::_nil();
}
throw quickstart::NoSuchAccount(account_name);
return 0;
}

AccountImpl::AccountImpl(const char* account_name,
quickstart::Storage* storage)
{
_account_name = CORBA::strdup(account_name);
_storage = quickstart::Storage::_duplicate(storage);
}

void
AccountImpl::markForRollback()
{
try
{
CORBA::ORB_var orb = CORBA::ORB_init();
CORBA::Object_var initRef =
orb->resolve_initial_references("TransactionCurrent");
CosTransactions::Current_var current =
CosTransactions::Current::_narrow(initRef);
current->rollback_only();
}
catch(const CosTransactions::NoTransaction&)
{
throw CORBA::TRANSACTION_REQUIRED();
}
}

CORBA::Float
AccountImpl::balance()
{
try
{
return _storage->balance(_account_name);
}

catch(const quickstart::NoSuchAccount& e)
{
cerr << "Account::balance: " << e << endl;
markForRollback();
return 0;
}
}

void
AccountImpl::credit(CORBA::Float amount)
{
if(amount < 0)
{
cerr << "Account::credit: Invalid amount: " << amount << endl;
markForRollback();
}
try
{
_storage->credit(_account_name, amount);
}
catch(const quickstart::NoSuchAccount& e)
{
cerr << "Account::credit: " << e << endl;
markForRollback();
}
}

void
AccountImpl::debit(CORBA::Float amount)
{
if(amount < 0 || balance() - amount < 0)
{
cerr << "Account::debit: Invalid amount: " << amount << endl;
markForRollback();
}
try
{
_storage->debit(_account_name, amount);
}
catch(const quickstart::NoSuchAccount& e)
{
cerr << "Account::debit: " << e << endl;
markForRollback();
}
}