VisiTransact Guide : Creating and propagating VisiTransact-managed transactions

Creating and propagating VisiTransact-managed transactions
This section focuses on using the Current interface in VisiTransact-managed transactions. It includes information about how to gain access to a VisiTransact-managed transaction with Current, and begin, rollback, and commit the transaction using the methods in the Current interface. It also explains how transactional objects can share in a VisiTransact-managed transaction.
Introducing Current as used in VisiTransact-managed transactions
With VisiTransact-managed transactions you are using the Current interface for all transaction management. You are beginning transactions using Current and you are using Current for the implicit transaction propagation. This means that you will always originate your transactions using Current::begin().
Current is an object that is valid for the entire process and manages the association of each thread's transaction context. Each thread has its own independent, isolated association with a transaction context.
In VisiTransact-managed transactions, transaction participants share the same transaction context because VisiTransact transparently forwards the transaction context to each participant. This means that the state of a transaction is maintained as the originator calls on other objects to perform actions, which may in turn call other objects.
Figure 8
1
2
3
4
In all four steps, the VisiTransact Transaction Service automatically and transparently propagates the transaction context between the transactional objects. If the first transactional object makes a subsequent request of another object, the transaction context flows on to this second object, as is shown in steps 1 to 4 in the figure above. If an object is not a transactional object, it does not receive the context, and, therefore, it cannot forward the context to any other object.
How does Current work?
VisiTransact-managed transactions are made possible with the Current object. The Current interface defines methods that simplify transaction management for most applications.
The Current interface is supported by a pseudo object whose behavior depends upon, and may alter, the transaction context associated with the invoking thread. Because Current is not a CORBA object, it cannot be accessed remotely.
Figure 9
A new transaction created with the begin() method is associated with the specific thread that called the method. A thread can be associated with only one transaction at a time. If a thread exits, or if the transaction originator's thread returns without completing the transaction, then any active transaction left associated with the thread will timeout and be rolled back.
Note
The application does not need to implement critical sections to ensure synchronization between threads when using the Current object.
Obtaining a Current object reference
For C++
To gain access to a VisiTransact-managed transaction, you must obtain an object reference to the Current object. The Current object reference is valid throughout the process. The following steps describe the general process for obtaining a reference to a Current object, and include code examples.
1
Call the ORB resolve_initial_references() method. This method obtains a reference to the Current object.
2
Narrow the returned object to a CosTransactions::Current or VISTransactions::Current object.
When you narrow to CosTransactions::Current, you specify your use of the original set of methods provided by the CosTransactions module. When you narrow to VISTransactions::Current, you specify the original set and the extensions to the Current interface provided by VisiTransact.
See “Extensions to the Current interface” for descriptions of VisiTransact extensions to the Current interface.
The following example shows examples of these alternatives in C++.
// To use OMG-compliant methods and behavior
CORBA::Object_var
obj = orb->resolve_initial_references("TransactionCurrent");
CosTransactions::Current_var
current = CosTransactions::Current::_narrow(obj);
// To use OMG behavior on CosTransactions methods and also use the
// additional VisiTransact methods
CORBA::Object_var
obj = orb->resolve_initial_references("TransactionCurrent");
VISTransactions::Current_var
current = VISTransactions::Current::_narrow(obj);
For Java
However for Visibroker for Java, you need to resolve to Current every time you access it in another thread, to gain access to a VisiTransact-managed transaction.
The following steps describe the behavioral difference between Java and C++ OTS:
When a user calls send_deferred() during a transaction, the resultant behavior is indeterminate.
This is because in Visibroker for Java implementation looks for a transaction service instance only upon the first transaction related call (for example, Current::begin()).
Working with the Current interface and its methods
The Current interface offers several methods for managing the current thread or context's transaction. The table below describes these methods.
See Extensions to the Current interface for descriptions of VisiTransact extensions to the Current interface.
Creates a new transaction. The SubtransactionsUnavailable exception will be raised if a transaction is already in progress. The transaction created will have a timeout from the last call to set_timeout(). If set_timeout() is not issued, the default timeout value of the VisiTransact Transaction Service is used.
Note
If you use get_control(), suspend() or resume(), it might affect checked behavior. For more information, see “How does the VisiTransact Transaction Service ensure checked behavior?”.
As shown in the following example, you can use the methods shown in the table above to perform actions with VisiTransact-managed transactions. This example shows the MyBank interface for the transactional object which defines the withdraw() method.
#include <CosTransactions.idl>
interface MyBank
{
float balance(in long accountNo);
boolean  withdraw(in long accountNo, in float amount);
};
The next example shows an example of an originator beginning a transaction and calling the withdraw() method on the MyBank transactional object. Then the originator either commits or rolls back the transaction.
...
// get object reference to my object implementation
MyBank_var bank = MyBank::_bind();

// start a transaction
current->
begin();

if(bank->withdraw(10, 444))
{
// invoke a CORBA request
current->commit(0);
}
else
{
current->rollback();
}
...
If an originator begins a transaction, it must commit or rollback the transaction. The VisiTransact Transaction Service will rollback the transaction if it times out. For example, a situation when a transaction may time out is if the originator's thread dies before the transaction is committed or rolled back.
Multiple threads participating in the same transaction
If you have a process and want to use multiple threads in the same transaction, you must pass the transaction context to each of the threads. In the typical scenario, you will start with a thread that has the transaction context—either because it is the originator and invoked Current::begin(), or because an operation passed the transaction context to it (implicitly or explicitly) and it needs to propagate that context to the other threads. This can be achieved by making the transaction's Control object available to the other threads and they can invoke Current::resume() specifying that Control object. Note that VisiTransact cannot provide checked behavior in this case.
Using multiple transactions within a context or thread
Note
This release of the VisiTransact Transaction Service does not support nested transactions. However, the procedure described in this section can be used to enable multiple transactions per thread or context.
You can manage multiple transactions within a thread; however, a thread can have only one active transaction at a time. The suspend() method is used to disassociate the current context, and resume() is used to associate another context. The table in “Working with the Current interface and its methods” describes the methods used to implement multiple transactions within a thread.
The following example shows an example of an object that originates multiple transactions from within a thread. This example illustrates that the MyBank_impl::withdraw() method can suspend the transaction in which the method was called, start a new transaction, and then resume the earlier transaction.
CORBA::Boolean MyBank_impl::withdraw( CORBA::Long accountNo,
CORBA::Float amount)
{
try
{
// check to see if a transaction has been started
CORBA::Object_var
obj = orb->resolve_initial_references("TransactionCurrent");
CosTransactions::Current_var
current = CosTransactions::Current::_narrow(obj);
// Suspend the current transaction. If there is no current transaction,
// the control will be null.
CosTransactions::Control_var control = current->
suspend();
// start a new transaction
try
{
current->begin();
// do your logic
current->commit(0);
}
catch(...)
{
// resume earlier transaction
current->resume(control);
throw;
}
}
catch(..) { }
}
Discovering an instance of the VisiTransact Transaction service
By default, the first time you start a transaction with begin() an instance of the VisiTransact Transaction Service is found using the Smart Agent. For details on the Smart Agent, see the VisiBroker Developer's Guide.
You can control the instance of the VisiTransact Transaction Service used with arguments passed to ORB_init(), or by how you set the VISTransactions::Current interface arguments. The Current arguments will override any arguments passed to ORB_init(). The arguments will only take effect for subsequent transactions that use Current::begin().
The arguments that you can set are:
Host Name. The Smart Agent will find any available VisiTransact Transaction Service instance that is located on the specified host.
VisiTransact Transaction Service Name. The Smart Agent will find the named VisiTransact Transaction Service instance anywhere on the network.
IOR. VisiTransact uses the specified IOR for the requested Transaction Service (CosTransactions::TransactionFactory) to locate the desired instance of a Transaction Service implementation on the network. This argument enables VisiTransact to operate without the use of a Smart Agent (osagent).
If you specify a combination of Host Name and VisiTransact Transaction Service Name, the Smart Agent will find the named VisiTransact Transaction Service instance on the named host. If you specify the IOR with either the Host Name or VisiTransact Transaction Service Name, the Smart Agent will find the VisiTransact Transaction Service instance by IOR only—it ignores the Host Name and VisiTransact Transaction Service Name.
The following table lists the arguments you can use to specify an instance of the VisiTransact Transaction Service.
The following example shows how to specify an instance of the VisiTransact Transaction Service by name using the ots_name argument of the VISTransactions::Current interface.
...
CORBA::Object_var obj =
orb->resolve_initial_references("TransactionCurrent");
CosTransactions::Current_var current =
VISTransactions::Current::_narrow(obj);

// to set the VisiTransact Transaction Service instance
current->ots_name("MyTxnSvc");
...
Propagating VisiTransact-managed transactions
To enable implicit propagation, a participant must be a transactional object—it must inherit from CosTransactions::TransactionalObject or define the OTSPolicy object with either the REQUIRE or ADAPT value. To enlist another participant in a transaction, the object enlisting the other participant must have a transaction associated with the current thread.
There are three ways a transaction is associated with the current thread:
Ensuring a transaction is in progress
If a participant requires a transaction, it should verify that a transaction is not currently in progress before beginning a new transaction. If a participant attempts to begin a new transaction when a transaction is already running, the VisiTransact Transaction Service throws a CosTransactions::SubtransactionsUnavailable exception. A participant that begins a new transaction must also rollback or commit the transaction before returning.
The following example illustrates how a server object ensures that its work is done as a transaction and avoids starting a new transaction when a transaction is already in progress.
CORBA::Boolean MyBank_impl::withdraw( CORBA::Long accountNo,
CORBA::Float amount)
{
// get ORB instance
CORBA::ORB_ptr orb = CORBA::ORB_init();

// get Current reference
CORBA::Object_var
obj = orb->resolve_initial_references("TransactionCurrent");
CosTransactions::Current_var
current = CosTransactions::Current::_narrow(obj);

CORBA::Boolean startFlag = 0;//use to signal creation of the transaction
CORBA::Boolean status = 0;
try
{
// check to see if a transaction has been started
if(current->get_status() == CosTransactions::StatusNoTransaction)
{
current->begin();
startFlag = 1; //we started and now own the current transaction
}
if(balance(accountNo) > amount)
{
// withdraw logic
...
status = 1;
}
}
catch(...) { }
if(startFlag && status)
{
current->commit();
}
else if(startFlag)
{
current->rollback();
}
return status;
}
Marking a transaction for rollback
When using Current, only an originator can terminate the transaction with commit() or rollback(). In this case, if a participant does not want the transaction to commit, it can use the rollback_only() method from the Current interface. When the rollback_only() method is called by a participant, the transaction associated with the target object is modified so that the only possible outcome is to rollback the transaction.
When invoking rollback_only(), the CosTransactions::NoTransaction exception is raised if there is no transaction in progress. The following example shows how a participant would use the rollback_only() method.
...
CosTransactions::Current_var current;
current->rollback_only();
...
Obtaining transaction information
A participant can obtain information about the current transaction such as its transaction name or transaction status using methods in the Current interface. The following table discusses these methods.
The get_status() method can return one of the following values:
Extensions to the Current interface
VisiTransact has an extended interface that provides arguments for specifying an instance of the VisiTransact Transaction Service, as well as additional methods. See “Discovering an instance of the VisiTransact Transaction service” for information on the VISTransactions::Current arguments. The following table shows the methods in the VisiTransact-extended Current interface in the VISTransactions.idl file. For more information about the Current interface, see Current interface in the VisiBroker for C++ API Reference.
Enables its caller to pass a user-defined informational transaction name. For example, this helps with diagnostics because the user-defined transaction name is included in the value returned by the get_transaction_name() method. The name also helps with administration, because the Console will report the name in the detailed information about an outstanding transaction.
Returns a PropagationContext which can be used by one VisiTransact Transaction Service domain to export a transaction to a new VisiTransact Transaction Service domain.
Provides the object transaction ID (otid) through the Current interface as a convenience. This avoids going to the Coordinator and looking through a PropagationContext. The otid is used to identify a transaction to a recoverable object. Most applications will not normally call this method.