This chapter presents an overview of one way in which you can call a BEA Tuxedo service from within an object managed by a CORBA server application, using the Wrapper sample application as an example.
This topic includes the following sections:
The Wrapper sample application delegates a set of billing operations to a BEA Tuxedo ATMI teller application, which contains a set of services that perform basic billing procedures. The approach in this chapter shows one technique for incorporating a BEA Tuxedo application into a BEA Tuxedo domain.
The examples shown in this chapter demonstrate a one-to-one relationship between operations on a CORBA object and calls to specific services within an application. In a sense, the calls to the BEA Tuxedo services are wrapped as operations on a CORBA object; thus, the object delegates its work to the BEA Tuxedo application. If you have a set of BEA Tuxedo services that you want to use in a CORBA server application, the technique shown in this chapter may work for you.
This chapter does not provide any details about BEA Tuxedo ATMI applications. For information about how to build and configure BEA Tuxedo ATMI applications, and for information about how they work, see the BEA Tuxedo ATMI information set, which is included in the BEA Tuxedo online documentation.
The process described in this chapter for wrapping a set of BEA Tuxedo services encompasses the following steps:
The following figure shows a high-level view of the relationship among the client application, the CORBA object managed by the CORBA server application, and the BEA Tuxedo ATMI application that implements the services called from the CORBA object.
The first step described in this chapter is designing the object that wraps the calls to the BEA Tuxedo ATMI application. For example, the goal for the Wrapper sample application is to add billing capability to the student registration process, which can be done by delegating a set of billing operations to an existing BEA Tuxedo ATMI teller application.
The BEA Tuxedo ATMI teller application used by the Wrapper sample application contains the following services:
To wrap these services, the Wrapper sample application includes a separate OMG IDL file that defines a new interface, Teller
, which has the following operations:
Each of these operations on the Teller
object maps one-to-one to calls on the services in the BEA Tuxedo ATMI teller application.
A typical usage scenario of the Teller
object may be the following:
register_for_courses()
operation on the Registrar
object, which requires a student ID.Registrar
object invokes the get_balance()
operation on the Teller
object, passing an account number.get_balance()
operation on the Teller
object puts the account number into a message buffer and sends the buffer to the BEA Tuxedo ATMI teller application's CURRBALANCE
service. CURRBALANCE
service. CURRBALANCE
service obtains from the University database the current balance of the account and gives it to the BEA Tuxedo ATMI teller application.Teller
object.Teller
object extracts the current balance amount from the message buffer and returns the current balance to the Registrar
object.
For more design information about the Teller
object and the Wrapper sample application, see the section "Design Considerations for the Wrapper Sample Application" on page -7.
The next step described in this chapter is creating the buffer within which messages are sent between the object and the BEA Tuxedo service. There are a number of buffer types that may be used by various BEA Tuxedo ATMI applications, and the examples used in this chapter are based on the FML buffer type. For more information about buffer types in the BEA Tuxedo system, see the BEA Tuxedo information set.
In your application implementation file, you need to allocate the chosen buffer type. You can allocate the buffer in the object's constructor, because the buffer you allocate does not need to be unique to any particular Teller
object instance. This allocation operation typically includes specifying the buffer type, passing any flags appropriate for the procedure call to the BEA Tuxedo service, and specifying a buffer size.
You also need to add to your implementation's header file the definition of the variable that represents the buffer.
The following code example shows the constructor for the Wrapper application's Teller
object that allocates the BEA Tuxedo buffer, m_tuxbuf
:
Teller_i::Teller_i() :
m_tuxbuf((FBFR32*)tpalloc("FML32", "", 1000))
{
if (m_tuxbuf == 0) {
throw CORBA::INTERNAL();
}
}
Note the following about the line that allocates the FML buffer:
The object's implementation file should also deallocate the buffer in the destructor, as in the following statement from the Wrapper application implementation file:
tpfree((char*)m_tuxbuf);
The next step is implementing the operations on the object that wraps calls to the BEA Tuxedo ATMI application. In this step, you choose the implementation of how the BEA Tuxedo services are called from the object. The Wrapper sample application uses the tpcall
implementation.
An operation on an object that wraps a BEA Tuxedo service typically includes statements that do the following:
The following example shows the implementation of the get_balance()
operation in the Wrapper application Teller
object. This operation retrieves the balance of a specific account, and the BEA Tuxedo service being called is CURRBALANCE
.
CORBA::Double Teller_i::get_balance(BillingW::AccountNumber account)
{
// "marshal" the "in" parameters (account number)
Fchg32(m_tuxbuf, ACCOUNT_NO, 0, (char*)&account, 0);
long size = Fsizeof32(tuxbuf);
// Call the CURRBALANCE Tuxedo service
if (tpcall("CURRBALANCE", (char*)tuxbuf, 0,
(char**)&tuxbuf, &size, 0) ) {
throw CORBA::PERSIST_STORE();
}
// "unmarshal" the "out" parameters (current balance)
CORBA::Double currbal;
Fget32(m_tuxbuf, CURR_BALANCE, 0, (char*)&currbal, 0);
return currbal;
}
The statement in the following code example fills the message buffer, m_tuxbuf
, with the student account number. For information about FML, see the BEA Tuxedo ATMI FML Function Reference.
Fchg32(m_tuxbuf, ACCOUNT_NO, 0, (char*)&account, 0);
The following statement calls the CURRBALANCE
BEA Tuxedo service, via the tpcall
implementation, passing the message buffer. This statement also specifies where the BEA Tuxedo service response is to be placed, which in this example is also the same buffer as the one in which the request was sent.
if (tpcall("CURRBALANCE", (char*)tuxbuf, 0,
(char**)&tuxbuf, &size, 0) ) {
throw CORBA::PERSIST_STORE();
}
The following statement extracts the balance from the returned BEA Tuxedo message buffer:
Fget32(m_tuxbuf, CURR_BALANCE, 0, (char*)&currbal, 0);
The last line in the get_balance()
operation returns the results to the client application:
return currbal;
Note the following restrictions regarding how you can incorporate BEA Tuxedo services within a BEA Tuxedo domain:
tpreturn()
or tpforward()
BEA Tuxedo implementations within an object that calls a BEA Tuxedo service.
The basic design considerations for the Wrapper sample application are based on the scenario that is described in this section. When a student registers for a course, the Registrar
object performs, as part of its registration process, invocations to the Teller
object, which charges the student's account for the course.
This section describes the design for the Wrapper sample application, which incorporates an additional server application, Billing, into the configuration. Therefore, the Wrapper sample application consists of the following four server applications:
In addition, the UBBCONFIG
file for the Wrapper sample application specifies the following groups:
The configuration of the BEA Tuxedo domain in the Wrapper sample application is shown in the following figure.
Incorporating a BEA Tuxedo ATMI application into the University sample applications makes sense from the standpoint of using the Process-Entity design pattern. BEA Tuxedo ATMI applications generally implement the Process-Entity design pattern, which are also used in the University sample applications.
The University database is updated to include a new table containing account information for each student. Therefore, when services in the BEA Tuxedo ATMI Teller Application process billing data, they perform transactions using the University database.
A typical usage scenario in the Wrapper sample application encompasses the following sequence of events:
get_student_details()
operation on the Registrar
object. Included in the implementation of the get_student_details()
operation is code that retrieves:Registrar
object, as with the Transactions sample application scenario, to invoke the register_for_courses()
operation. The request continues to include only a list of course numbers and a student ID.register_for_courses()
operation invokes:get_balance()
and debit()
operations on the Teller
object each send a request to the BEA Tuxedo ATMI Teller application. Encapsulated in the request is an FML buffer containing the appropriate calls, including the account number calls to, respectively, the CURRBALANCE
and DEBIT
services in the BEA Tuxedo ATMI Teller application.CURRBALANCE
and DEBIT
services perform the appropriate database calls to, respectively, obtain the current balance and debit the student's account to reflect the charges for the courses for which he or she has registered.
If the student has a delinquent account, the Registrar
object returns the DelinquentAccount
exception to the client application. The client application then rolls back the transaction.
If the debit()
operation fails, the Teller
object invokes the rollback_only()
operation on the TransactionCurrent object. Because the Teller
and Registrar
objects are scoped within the same transaction, this rollback affects the entire registration process and thus prevents the situation where there is an inconsistent database (showing, for example, that the student is registered for the course, but the student's account balance has not been debited for the course).
Registrar
object registers the student for the desired courses.The following interface definitions are defined for the Billing server application:
The following additional considerations influence the design of the Wrapper sample application:
Registrar
object needs a way to send requests to the Teller
object to handle billing operations.
Both of these considerations have implications on the UBBCONFIG
file for the Wrapper sample application. The following sections discuss these and other additional design considerations in detail.
Up until now, all the objects in the University server application have been defined in the same server process. Therefore, for one object to send a request to another object is fairly straightforward, and is summarized in the following steps, using the Registrar
and CourseSynopsisEnumerator
objects as an example:
Registrar
object creates an object reference to the CourseSynopsisEnumerator
object. Registrar
object sends the request to the CourseSynopsisEnumerator
object.CourseSynopsisEnumerator
object is not in memory, the TP Framework invokes the Server::create_servant()
operation on the Server object to instantiate the CourseSynopsisEnumerator
object.However, now that there are two server processes running, and an object in one process needs to send a request to an object managed by the second process, the procedure is not quite so straightforward. For example, the notion of getting an object reference to an object in another server process has important implications. For one, the second server process has to be running when the request is made. Also, the factory for the object in the other server process must be available.
The Wrapper sample application addresses this by incorporating the following configuration and design elements:
TellerFactory
object in the University Server object's Server::initialize()
operation. The University server application then caches the TellerFactory
object reference. This introduces a performance optimization because, otherwise, the Registrar
object would need to do the following each time it needs a TellerFactory
object:Registrar
object subsequently invokes the TellerFactory
object, the Registrar
object uses the object reference acquired by the Server::initialize()
operation (described in the preceding list item). You specify in the UBBCONFIG
file the order in which server processes are started.register_for_courses()
and get_student_details()
operations on the Registrar
object are modified to include code that invokes operations on the Teller
object.
The Wrapper sample application is designed to handle the situation in which the amount owed by the student exceeds the maximum allowed. If the student tries to register for a course when he or she owes more than is permitted by University, the Registrar
object generates a user-defined DelinquentAccount
exception. When this exception is returned to the client application, the client application rolls back the transaction. For information about how to implement user-defined exceptions, see the section "User-defined Exceptions" on page -17.
Another consideration that affects the performance of the Wrapper sample application is setting the appropriate transaction policies for the interfaces of the objects in that application. The Registrar
, CourseSynopsisEnumerator
, and Teller
objects are configured with the always
transaction policy. The RegistrarFactory
and TellerFactory
objects are configured with the ignore
transaction policy, which prevents the transactional context from being propagated to these objects, which do not need to be included in transactions.
As mentioned earlier, the Billing server application is configured in a group separate from the group containing the University database and the University application, BEA Tuxedo ATMI Teller application, and Oracle Transaction Manager Server (TMS) application.
However, since the Billing server application participates in the transactions that register students for courses, the Billing server application must include invocations to the TP::open_xa_rm()
and TP::close_xa_rm()
operations in the Server object. This is a requirement for any server application that manages an object that is included in any transaction. If that object does not perform any read or write operations on a database, you can specify the NULL resource manager in the following locations:
For information about building, configuring, and running the Wrapper sample application, see the Guide to the CORBA University Sample Applications.