Code Examples

The main purpose of this code example is to highlight and explain how to write application logic using the C++ framework generated by the C++ Expert.

This section describes the following parts of the sample application:

The sample application files installed with the C++ Expert are fully annotated and explain the actions of each section of code. The files containing the source code for this walk-through can be found in the NoDB (no database) sample for the C++ Expert. The NoDB sample uses an in-memory database instead of accessing an external database. By default, these files can be found at
YourDrive:TUXBldr\Samples\CPPExpert\NoDB (Windows NT)
<Builder_Install_dir>/client/Samples/CPPExpert/NoDB (UNIX)

In the Samples\CPPExpert directory, you will also find a directory called Ora. This sample uses Oracle 7.3.3 as its database.

Two types of comments appear throughout the sample application code:

Keep in mind that the sample application code is presented as an example of how you can use the C++ Expert API and generated framework, and does not necessarily imply application design recommendations. There are many ways to leverage the sample code to fit your own design techniques, preferences, and the requirements of your particular application.
 

Implementing a Service

The following sections show how the Open Account service is implemented and used:

The BackOfficeImpl.cpp file is generated by the C++ Expert. The business logic for the Open Account service is implemented within this file within the protected code section.

Warning: Any code that you add to the files generated by the C++ Expert must be placed between the protected section markers. This prevents subsequent generation of sample application code from overwriting your additions. Each section name is unique and should not be altered.

Listing 5-2 Generated Signature of the Service Member Function
BANKAPP::BACKOFFICE::OPEN_ACCTReply*
BACKOFFICEImpl::OPEN_ACCT(long BRANCH_ID,
const char* SAMOUNT, char ACCT_TYPE, const char* LAST_NAME,
const char* FIRST_NAME, char MID_INIT, const char* SSN,
const char* ADDRESS, const char* PHONE, char OPEN_CR)
throw (BEATuxBldr::TuxException*, OPEN_ACCTException*)
{
BANKAPP::BACKOFFICE::OPEN_ACCTReply* pReply;

// $[BEGIN_PROTECTED_SECTION]:BANKAPP_BACKOFFICE_OPEN_ACCT_5

...

// $[END_PROTECTED_SECTION]

return pReply;
}


 

Declare Variables and Output Parameters

The following code declares some variables and output parameters used in the OPEN_ACCT service.

Listing 5-3 Declare Variables and Output Parameters
    char *STATLIN;
long lNewAccountId;
float fAmount;
char SBALANCE[10];


 

Transform Input Parameters for OPEN_ACCT

This code transforms the format of some input parameters for use with the Account and OPEN_ACCTReply object.

Listing 5-4 Transform Input Parameters for OPEN_ACCT
    sscanf(SAMOUNT,"%f",&fAmount);
sprintf(SBALANCE,"$%.2f",fAmount);


 

Add a New Account to the In-memory Database

The Account and AccountFactory objects are used to simulate a persistent storage mechanism, since no database is used with this sample. These objects are not generated by the C++ Expert.

This code creates an Account object using input parameters we declared as well as the input parameters found in signature of the service (see Listing 5-2). This object is then added to the in-memory database using the AccountFactory.

Listing 5-5 Add a New Account to the In-memory Database
    Account * pAccount = new Account
( 0, BRANCH_ID, SSN, fAmount,
ACCT_TYPE, LAST_NAME, FIRST_NAME,
MID_INIT, PHONE,
ADDRESS );

long stat = AccountFactory::Instance()->createAccount(
pAccount,
&lNewAccountId );


 

Check for Application Errors

If the createAccount member function invocation returns an error, we create and throw the OPEN_ACCTException exception with a description of the error. This exception will be caught by the client that makes the call to the OPEN_ACCT service.

Listing 5-6 Check for Application Errors
    if ( stat == AccountFactory::AccountDBfull ) {
STATLIN = "Account data base is full";
throw new OPEN_ACCTException(STATLIN);
} else {
STATLIN = "Success";
}


 

Send the Reply to the Client

If createAccount does not return an error, we create a reply object (OPEN_ACCTReply) containing the output data to be returned to the client and return.

Listing 5-7 Send the Reply to the Client
    pReply = new BANKAPP::BACKOFFICE::OPEN_ACCTReply(lNewAccountId, 
SBALANCE, STATLIN);
return pReply;


 

Making a Synchronous Call

The Do_OPEN_ACCT function in the file BankAppClient.cpp provides an example of how to make synchronous requests using the generated framework. This function performs synchronous calls to the OPEN_ACCT service.

The following sections show how a synchronous call to the Open Account service is implemented and used:


 

Create the Service Request Object

We create the BACKOFFICE service request object, which contains the OPEN_ACCT and CLOSE_ACCT member functions that provide access to the corresponding services. In this example, we focus on the OPEN_ACCT service. The CLOSE_ACCT service is implemented in a similar fashion in the BankAppClient.cpp file.

Listing 5-8 Create Service Request Object
BACKOFFICE *pBACKOFFICE;

pBACKOFFICE = new BACKOFFICE();


 

Declare Parameters

The following code declares parameters used for calling the OPEN_ACCT service.

Listing 5-9 Declare Parameters
long ACCOUNT_ID;
char SAMOUNT[12];
char SBALANCE[12];
long BRANCH_ID;
char ACCT_TYPE;
char LAST_NAME[21];
char FIRST_NAME[21];
char MID_INIT;
char SSN[12];
char ADDRESS[61];
char PHONE[13];
char OPEN_CR;


 

Prompt for Input Parameters for OPEN_ACCT

Next, we prompt the user for information about the function they requested. The values are assigned to the input parameters in preparation for our synchronous call to the OPEN_ACCT service in the BANKAPP server.

Listing 5-10 Prompt for the Input Parameters for OPEN_ACCT
    cout << "\n--- OPEN_ACCT operation" << endl;

cout << "\nEnter BRANCH_ID(nn): " << endl;
gets (szInputBuffer);
BRANCH_ID = (long)atol(szInputBuffer);

cout <<"Enter SSN(nnn-nn-nnnn): " << endl;
gets (SSN);

cout <<"Enter SAMOUNT(nnnnn.nn): " << endl;
gets (SAMOUNT);

cout <<"Enter ACCT_TYPE('C'/'S'): " << endl;
gets (szInputBuffer);
ACCT_TYPE = *szInputBuffer;

cout <<"Enter LAST_NAME(20 chars): " << endl;
gets (LAST_NAME);

cout <<"Enter FIRST_NAME(20 chars): " << endl;
gets (FIRST_NAME);

cout <<"Enter MID_INIT(1 char): " << endl;
gets (szInputBuffer);
MID_INIT = *szInputBuffer;

cout <<"Enter PHONE(nnn-nnn-nnnn): " << endl;
gets (PHONE);

cout <<"Enter ADDRESS(60 chars): " << endl;
gets (ADDRESS);

OPEN_CR = 'A';


 

Make a Synchronous Call to the OPEN_ACCT Service

We now open an account by making a call to the OPEN_ACCT service. We do not explicitly set the mode flag on the service request object because this object uses the synchronous mode of communication by default. (For more information about communications mode settings, see InvocationType in the API.)

The OPEN_ACCT member function returns a reply object (pOPEN_ACCTReply) containing the results of the service call.

When the call to the OPEN_ACCT service is made, we must check for exceptions by looking at values in TuxException and the generated exception (OPEN_ACCTException) associated with the service. The TuxException exception is thrown when the generated implementation code has detected a BEA TUXEDO system error. The generated exception is thrown when any application error occurs. For more information about how generated exceptions are named and used, see the Generated Exception section in the API.

Listing 5-11 Make a Synchronous Call to the OPEN_ACCT Service
    cout <<"Calling OPEN_ACCT..." << endl;
try {

pOPEN_ACCTReply = pBACKOFFICE->OPEN_ACCT(
BRANCH_ID,
SAMOUNT,
ACCT_TYPE,
LAST_NAME,
FIRST_NAME,
MID_INIT,
SSN,
ADDRESS,
PHONE,
OPEN_CR);

} catch (BANKAPP::BACKOFFICE::OPEN_ACCTException *e) {
cout << "\n--- OPEN_ACCTException:" << endl;
cout << "\n--- STATLIN=" << e->_STATLIN << endl;
delete e;
return;
} catch (BEATuxBldr::TuxException *te) {
cout << "\n--- TuxException=" << te->GetMessage() << endl;
delete te;
return;
}


 

Display the Results of the Service Reply

When the service call returns, we invoke the accessor functions of the pOPEN_ACCTReply object to determine the results. For each output parameter there is a specific function generated that returns the value of that output parameter. For example, the GetSBALANCE member function is used to get the value of the SBALANCE output parameter from the OPEN_ACCT service. For more information about reply objects, see the Generated Service Reply section in the API. For more information about the pOPEN_ACCTReply object, see the Make a Synchronous Call to the OPEN_ACCT Service section.

Listing 5-12 Display the Results of the Service Replies
    cout << "\nOutput ACCOUNT_ID: " <<
pOPEN_ACCTReply->GetACCOUNT_ID() << endl;
cout << "\nOutput SBALANCE: " <<
pOPEN_ACCTReply->GetSBALANCE() << endl;
cout << "\nOutput STATLIN: " <<
pOPEN_ACCTReply->GetSTATLIN() << endl;


 

Making Asynchronous Calls Within a Transaction

The Do_Transfer function in the file BankAppClient.cpp provides an example of how to use asynchronous communication and transaction demarcation with the generated framework. This function performs asynchronous calls to the DEPOSIT and WITHDRAWAL services within the scope of a transaction to perform a transfer of an amount from one account to another.

The following sections show how asynchronous calls to the WITHDRAWAL and DEPOSIT services within a transaction are implemented and used:


 

Create the Transaction and Service Request Objects

We create the Transaction object once at the beginning of the application with global scope so the Transaction is available to all modules from a single reference point.

We also create the TELLER service request object, which contains the WITHDRAWAL and DEPOSIT member functions. We will use these member functions to implement the TRANSFER function of the BANKAPP application.

Listing 5-13 Create the Transaction and Service Request Objects
TELLER *pTELLER;
BEATuxBldr::Transaction *pTrans;

pTELLER = new TELLER();
pTrans = new BEATuxBldr::Transaction();


 

Declare Parameters and Variables

Since the Do_Transfer function uses both the WITHDRAWAL and DEPOSIT services, we need to ensure that parameters are declared for those services. We also declare the reply object pointers for calls to WITHDRAWAL and DEPOSIT services.

Listing 5-14 Declare Parameters and Variables
    long To_ACCOUNT_ID;
long From_ACCOUNT_ID;
char SAMOUNT[12];

BANKAPP::TELLER::WITHDRAWALReply* pWITHDRAWALReply;
BANKAPP::TELLER::DEPOSITReply* pDEPOSITReply;


 

Start a Transaction

After a transaction is started, all subsequent service calls participate in that transaction until it is rolled back or committed. Calls made to the WITHDRAWAL and the DEPOSIT services are contained within a transaction to ensure database consistency. We start a transaction by calling the Do_BeginTransaction wrapper function to perform transaction demarcation, hide the exception handling and keep the main code cleaner.
 

Beginning a Transaction

In the sample code we have wrapped the call that begins the transaction within a function called Do_BeginTransaction. The NoTransaction and SubtransactionsUnavailable exceptions should be handled by any application that uses the Transaction::begin member function. The Do_BeginTransaction function allows the application to consistently handle exceptions that may be thrown by a Transaction.

Listing 5-15 Start a Transaction
int Do_BeginTransaction()
{
try {
pTrans->begin();
} catch (BEATuxBldr::NoTransaction *ne) {
cout << "\n--- Begin trans err; NoTransaction=" <<
ne->GetMessage() << endl;
delete ne;
return FALSE;
} catch (BEATuxBldr::SubTransactionsUnavailable *se) {
cout << "\n--- Begin trans err;
SubTransactionsUnavailable=" <<
se->GetMessage() << endl;
delete se;
return FALSE;
}
return TRUE;
}


 

Prompt for Input Parameters for WITHDRAWAL and DEPOSIT

We now prompt the user for input and assign returned values to parameters in preparation for our asynchronous call to the WITHDRAWAL and DEPOSIT service in the BANKAPP server.

Listing 5-16 Prompt for Input Parameters for WITHDRAWAL and DEPOSIT
    cout << "\n--- TRANSFER operation" << endl;

cout << "\nEnter From ACCOUNT_ID(nnnnnn): " << endl;
gets (szInputBuffer);
From_ACCOUNT_ID = (long)atol(szInputBuffer);

cout <<"Enter To ACCOUNT_ID(nnnnnn): " << endl;
gets (szInputBuffer);
To_ACCOUNT_ID = (long)atol(szInputBuffer);

cout <<"Enter SAMOUNT(nnnnn.nn): " << endl;
gets (SAMOUNT);


 

Make Asynchronous Calls to the WITHDRAWAL and DEPOSIT Services

The TRANSFER transaction will be performed by making calls to the WITHDRAWAL service and the DEPOSIT service, in that order. These calls are made asynchronously. We must explicitly set the mode flag to ASYNCHRONOUS on the service request object to specify this mode each time. (For more information about communications mode settings, see InvocationType.) The calls made to the WITHDRAWAL and DEPOSIT services are contained within a transaction to ensure database consistency.

When the calls to WITHDRAWAL and DEPOSIT return, we must check for exceptions by looking at values contained in TuxException. This exception is thrown when the generated implementation code has detected a BEA TUXEDO system error. If any exceptions occur, we must rollback any active transaction. We handle this exception with in-line code in the sample application instead of calling another function.

We must also check for exceptions on the rollback. To keep the code more readable, the exception processing on the rollback is placed in a separate function (see the Rolling back a Transaction section).

Listing 5-17 Make Asynchronous Calls to the WITHDRAWAL and DEPOSIT Services
    if (!Do_BeginTransaction()) return;

cout <<"Calling WITHDRAWAL..." << endl;

pTELLER->SetInvoke(BEATuxBldr::ASYNCHRONOUS);

try {

pWITHDRAWALReply = pTELLER->WITHDRAWAL(
From_ACCOUNT_ID,
SAMOUNT);

} catch (BEATuxBldr::TuxException *te) {
cout << "\n--- Withdrawal TuxException=" <<
te->GetMessage() << endl;
delete te;
//*** Rollback active transaction
Do_RollbackTransaction();
return;
}

pTELLER->SetInvoke(BEATuxBldr::ASYNCHRONOUS);

cout << "\nCalling DEPOSIT..." << endl;
try {

pDEPOSITReply = pTELLER->DEPOSIT(
To_ACCOUNT_ID,
SAMOUNT);

} catch (BEATuxBldr::TuxException *te) {
cout << "\n--- Deposit TuxException=" <<
te->GetMessage() << endl;
delete te;
Do_RollbackTransaction();
return;
}


 

Rolling back a Transaction

In the sample code we have wrapped the call that rolls back a transaction in the Do_RollbackTransaction function. The NoTransaction exception should be handled by any application that uses the Transaction::rollback member function. The Do_RollbackTransaction function allows the application to consistently handle exceptions that may be thrown by a Transaction.

Listing 5-18 Rolling Back a Transaction
int Do_RollbackTransaction()
{
try {
pTrans->rollback();
} catch (BEATuxBldr::NoTransaction *ne) {
cout << "\n--- Rollback trans err; NoTransaction=" <<
ne->GetMessage() << endl;
delete ne;
return FALSE;
}
return TRUE;
}


 

Polling for and Collecting Pending Replies

If a reply to an asynchronous request is available, it must be explicitly collected by the client. The sample application determines when a reply is available by polling. We do this for replies to both the DEPOSIT and WITHDRAWAL service.

The first thing we do is ensure that the calls to the ReplyCompleted member function return immediately and do not block by setting the TPNOBLOCK flag on the pDEPOSITReply and pWITHDRAWALReply objects. When the reply is ready, the call to ReplyCompleted returns TRUE and we will use the generated accessor functions of pWITHDRAWALReply and pDEPOSITReply to access the returned output parameters.

When we invoke the ReplyCompleted member functions of pWITHDRAWALReply and pDEPOSITReply to collect the reply, we must also check for exceptions by looking at values in TuxException and the exception generated for this service (see the Generated Exception section in the API). The following exceptions are associated with services we will be using:

Table 5-2 Exceptions Generated for the Sample Application

Service Generated Exception

WITHDRAWAL

WITHDRAWALException

DEPOSIT

DEPOSITException

(

These exceptions are handled with in-line code, instead of calling another function in the sample application. If an exception was thrown, we have to roll back the active transaction and check for exceptions on the rollback. To keep the code more readable, the exception processing on the rollback is placed in a separate function. In the sample code we have wrapped the call that rolls back the transaction within a function called Do_RollbackTransaction. The Rolling back a Transaction section shows the code for the Do_RollbackTransaction function.

Listing 5-19 Polling for and Collecting Pending Replies
    BEATuxBldr::Boolean DEPOSITDone = false;
BEATuxBldr::Boolean WITHDRAWALDone = false;

pDEPOSITReply->SetFlags(TPNOBLOCK);
pWITHDRAWALReply->SetFlags(TPNOBLOCK);

while (( ! DEPOSITDone) || ( ! WITHDRAWALDone)) {

try {
DEPOSITDone = pDEPOSITReply->ReplyCompleted();
} catch (BANKAPP::TELLER::DEPOSITException *e) {
cout << "\n--- DEPOSITException:" << endl;
cout << "\n--- STATLIN=" << e->_STATLIN << endl;
delete e;
Do_RollbackTransaction();
return;
} catch (BEATuxBldr::TuxException *te) {
cout << "\n--- Deposit TuxException="
<< te->GetMessage() << endl;
delete te;
Do_RollbackTransaction();
return;
}

try {
WITHDRAWALDone = pWITHDRAWALReply->ReplyCompleted();
} catch (BANKAPP::TELLER::WITHDRAWALException *e) {
cout << "\n--- WITHDRAWALException:" << endl;
cout << "\n--- STATLIN=" << e->_STATLIN << endl;
delete e;
Do_RollbackTransaction();
return;
} catch (BEATuxBldr::TuxException *te) {
cout << "\n--- Withdrawal TuxException="
<< te->GetMessage() << endl;
delete te;
Do_RollbackTransaction();
return;
}
}


 

End a Successful Transaction

If the calls to the WITHDRAWAL and DEPOSIT services complete successfully, we need to end the transaction by invoking the Transaction::commit member function.

Listing 5-20 End a Successful Transaction
Do_CommitTransaction();


 

Committing a Transaction

In the sample code we have wrapped the call that commits the transaction within a function called Do_CommitTransaction. The HeuristicHazard, HeuristicMixed, and NoTransaction exceptions must be handled by any application that uses the Transaction::commit member function. The Do_CommitTransaction function allows the application to consistently handle exceptions thrown by a Transaction.

Listing 5-21 Committing a Transaction
int Do_CommitTransaction()
{
try {
pTrans->commit(FALSE);
} catch (BEATuxBldr::NoTransaction *ne) {
cout << "\n--- Commit trans err; NoTransaction=" <<
ne->GetMessage() << endl;
delete ne;
return FALSE;
} catch (BEATuxBldr::HeuristicHazard *he)
{
cout << "\n--- Commit trans err; HeuristicHazard=" <<
he->GetMessage() << endl;
delete he;
return FALSE;
} catch (BEATuxBldr::HeuristicMixed *hme) {
cout << "\n--- Commit trans err; HeuristicMixed=" <<
hme->GetMessage() << endl;
delete hme;
return FALSE;
}
return TRUE;
}


 

Display the Results of the Service Replies

When the service call returns, we invoke the accessor functions of the pWITHDRAWALReply and pDEPOSITReply reply objects to access the output parameters. (For more information about the reply objects, see the Polling for and Collecting Pending Replies section.) For each output parameter there is a specific member function generated that returns the value of that output parameter. For example, the GetSBALANCE member function is used to get the value of the SBALANCE output parameter from the WITHDRAWAL service.

Listing 5-22 Display the Results of the Service Replies
    cout << "\nOutput From SBALANCE: " <<
pWITHDRAWALReply->GetSBALANCE() << endl;
cout << "\nOutput From STATLIN: " <<
pWITHDRAWALReply->GetSTATLIN() << endl;

cout << "\nOutput To SBALANCE: " <<
pDEPOSITReply->GetSBALANCE() << endl;
cout << "\nOutput To STATLIN: "<<
pDEPOSITReply->GetSTATLIN() << endl;