With the Transaction Service, this is achieved by implementing CosTransactions::Resource objects -- each resource represents a local transaction in the transactional system -- and registering these Resource objects with the distributed transactions.
Since many systems provide a standard interface to their transactional
capabilities -- the XA interface -- it is possible to implement CosTransactions::Resource
objects on top of the XA interface, and provide an easy to use integration
with the Transaction Service. The same integration (with the same interfaces
and behavior) may also be provided through proprietary interfaces provided
by a given Transaction Service implementation, without the creation and
registration of CosTransactions::Resource objects. See Figure 1.
The Java Transaction API Specification [JTA] defines the Java equivalent of the XA interface (javax.transaction.xa.XAResource) and a set of local Java interfaces that provide a "higher level" API to the Transaction Service (the interfaces are defined in the javax.transaction package). JTA also specifies the standard integration between the Transaction Service and Resource Managers that implement the Java XAResource interface.
For implementations in Java, JTA when available, is the preferred standard integration API between the Transaction Service and XA Resource Managers. Note, JTA is the standard Transaction Service/XA Resource Manager integration API for implementations in Java compatible with the J2EE platform [J2EE].
This section specifies the interfaces for the standard integration between the Transaction Service and C XA resource managers [1]. Unlike JTA, this section does not define a higher level API to the Transaction Service: it relies directly on the Transaction Service types defined in the CosTransactions module.
This XA integration can be implemented using the standard Transaction
Service interfaces; as a result, it may be provided by a Transaction Service
vendor, a Resource Manager vendor, or any other third party. Likewise
the integration with Resource Managers that implement the Java XAResource
interface can be provided by a Transaction Service vendor, a Resource Manager
vendor, or any other third party. A compliant Transaction Service implementation
may, but does not need to, provide any or both of these standard integrations.
XA defines a set of C-function pointers, and a C-struct that holds these function pointers, xa_switch_t:
/*
* From Appendix A of the XA specification:
*/
struct xa_switch_t {
char name[RMNAMESZ];
/* name of resource manager */
long flags;
/* resource manager specific options */
long version;
/* must be 0 */
int (*xa_open_entry)
/* xa_open function pointer */
(char *, int, long);
int (*xa_close_entry)
/* xa_close function pointer */
(char *, int, long);
int (*xa_start_entry)
/* xa_start function pointer */
(XID *, int, long);
int (*xa_end_entry)
/* xa_end function pointer */
(XID *, int, long);
int (*xa_rollback_entry) /*
xa_rollback function pointer */
(XID *, int, long);
int (*xa_prepare_entry)
/* xa_prepare function pointer */
(XID *, int, long);
int (*xa_commit_entry)
/* xa_commit function pointer */
(XID *, int, long);
int (*xa_recover_entry)
/* xa_recover function pointer */
(XID *, long, int, long);
int (*xa_forget_entry)
/* xa_forget function pointer */
(XID *, int, long);
int (*xa_complete_entry) /*
xa_complete function pointer */
(int *, int *, int, long);
};
Each XA-capable system must provide a global instance of xa_switch_t.
The function pointers provided by this xa_switch_t instances can be divided in four categories:
The main drawback of tying connections and threads is flexibility since
it prevents the application from managing connections independently of
threads, which limits a lot the kind of connection pooling that can be
implemented. Also, a CORBA server typically dispatches different requests
to different threads: the thread of control equal thread model prevents
the use of xa_end(TMSUSPEND) at the end of a request and xa_start(TMRESUME)
at the beginning of the next request in the same transaction, since an
association must be resumed by the thread of control by which it was suspended.
typedef short ThreadModel;
const ThreadModel PROCESS = 0;
const ThreadModel THREAD = 1;
local interface CurrentConnection
{
void
start(
// xa_start(TMNOFLAGS) or xa_start(TMJOIN)
in CosTransactions::Coordinator
tx,
in CosTransactions::otid_t
otid
);
void
suspend(
// xa_end(TMSUSPEND)
in CosTransactions::Coordinator
tx,
in CosTransactions::otid_t
otid
);
void resume(
// xa_start(TMRESUME)
in CosTransactions::Coordinator
tx,
in CosTransactions::otid_t
otid
);
void end(
// xa_end(TMSUCCESS) or xa_end(TMFAIL)
in CosTransactions::Coordinator
tx,
in CosTransactions::otid_t
otid,
in boolean
success
);
ThreadModel thread_model();
long rmid();
};
In order to do some work within a distributed transaction with a given XA resource manager, the application needs to associate the resource manager's current connection with this transaction (or more precisely a transaction branch which represents this transaction in the resource manager), by calling CurrentConnection::start:
// C++
// assuming the OTS transaction is associated with
the current thread
CosTransactions::Control_var control = tx_current->get_control();
CosTransactions::Coordinator_var tx = control->get_coordinator();
CosTransactions::PropagationContext_var ctx = tx->get_txcontext();
const CosTransactions::otid_t& otid = ctx->current.otid;
current_connection->start(tx, otid);
The first time start is called with a given otid on one of the CurrentConnection
objects associated with a ResourceManager, the ResourceManager creates
a transaction branch, creates a CosTransactions::Resource persistent object
representing this transaction branch and registers this object with the
given transaction coordinator. The otid parameter is transformed into an
XID and passed to xa_start(), unaltered.
Note: a compliant implementation does not need to create and register
a CosTransaction::Resource object, as long as the external behavior is
the same.
Once the application has finished using a connection, it needs to end the association with the transaction branch, for two reasons:
When the thread model is PROCESS, xa_open() is called by or before the
first start call; xa_close() is called during shutdown. When the
thread model is THREAD, each thread calls xa_open() the first time (or
before the first time) this thread executes CurrentConnection::start; xa_close()
is called when this thread exits.
In addition, the user may wish to alter the branch qualifier of the otid to either share the same transaction branch between different processes (tightly-coupled model) or use different transaction branches in processes using the same resource manager within the same distributed transaction (loosely-coupled model).
Figure 2 shows the components involved when the application creates a new association by calling start on a CurrentConnection object:
Figure 2: Creating a new Association
Figure 3: Association State Diagram
When start, suspend, resume or end raises CORBA::INTERNAL with the minor
code (TBD -- XAER_RMFAIL), the new state is
"non existent".
When resume, suspend or end raises CORBA::TRANSACTION_ROLLEDBACK with
the minor code (TBD -- XA_RB), the new state
is "non existent".
When end raises CORBA::TRANSACTION_ROLLEDBACK with the minor code
(TBD -- deferred rollback), the new state is "non existent".
For every other exceptions raised by start, suspend, resume and end, there is no state transition.
Note:
The PSS TransactionalSession interface has no resume operation: with
PSS, when start is called on a suspended association, the association is
resumed. Like PSS, JTA combines start and resume in a single method, Transaction.enlistResource().
Because of the CurrentConnection::resume operation, and implementation
of the CurrentConnection local interface does not need to maintain information
about the state of the underlying XA connection(s).
interface BeforeCompletionCallback
{
void
before_completion(
in CosTransactions::Coordinator
tx,
in CosTransactions::otid_t
otid,
in boolean
success
);
};
interface ResourceManager
{
unsigned long
register_before_completion_callback(in
BeforeCompletionCallback bcc);
void
unregister_before_completion_callback(in
unsigned long key);
};
A Resource Manager object manages transaction branches. An application can register any number (up to 2^31 -1) of BeforeCompletionCallback objects with a resource manager, to get notified each time a non-preprared transaction branch is about to be prepared, committed-in-one-phase or rolled back.
The only operation on BeforeCompletionCallback, before_completion, accepts the following parameters:
A typical use of a BeforeCompletionCallback object is to end a suspended association in a single-threaded server, as shown on Figure 4.
An application uses the operation register_before_completion_callback to registers a BeforeCompletionCallback with a ResourceManager. register_before_completion_callback returns an unsigned long that the application uses to unregister a BeforeCompletionCallback object.
Figure 4: Using a BeforeCompletionCallback to End a Suspended Association
Note 1: The primary advantage of BeforeCompletionCallback objects over CosTransactions::Synchronization objects is the number of CORBA requests per transaction: three for a synchronization (registration, before_completion, after_completion) versus only one for a BeforeCompletionCallback object.
Note 2: When starting and ending an association for each request, there is no need for any BeforeCompletionCallback object.
Note 3: The interfaces between the CurrentConnection objects, the ResourceManager object and the Resource objects (if there is any) are not specified. The diagram above illustrates a possible implementation. Other implementations are possible: for example a Resource object could register itself with the Transaction Coordinator in its constructor.
The Connector local interface is defined in the XA module as follows:
native XASwitch;
local interface Connector
{
ResourceManager
create_resource_manager(
in string
resource_manager_name,
in XASwitch
xa_switch,
in string
open_string,
in string
close_string,
in ThreadModel
thread_model,
in boolean
automatic_association,
in boolean
dynamic_registration_optimization,
out CurrentConnection
current_connection
);
CurrentConnection
connect_to_resource_manager(
in ResourceManager
rm,
in XASwitch
xa_switch,
in string
open_string,
in string
close_string,
in ThreadModel
thread_model,
in boolean
automatic_association,
in boolean
dynamic_registration_optimization
);
};
| Exception | Minor Code | Raised By | When |
| BAD_PARAM | (TBD) | Connector::create_resource_manager,
Connector::connector_to_resource_manager CurrentConnection::start, CurrentConnection::suspend, CurrentConnection::resume, CurrentConnection::end |
A xa_ call returns XAER_INVAL |
| BAD_INV_ORDER | (TBD) | CurrentConnection::start | A xa_start() call returns XAER_OUTSIDE |
| BAD_INV_ORDER | (TBD) | CurrentConnection::start,
CurrentConnection::suspend, CurrentConnection::resume, CurrentConnection::end |
A xa_ call returns XAER_PROTO |
| INTERNAL | (TBD) | Connector::create_resource_manager,
Connector::connector_to_resource_manager CurrentConnection::start, CurrentConnection::suspend, CurrentConnection::resume, CurrentConnection::end |
A xa_ call returns XAER_RMERR |
| INTERNAL | (TBD) | CurrentConnection::start,
CurrentConnection::suspend, CurrentConnection::resume, CurrentConnection::end |
A xa_ calls returns XAER_RMFAIL |
| TRANSACTION_ROLLEDBACK | (TBD) | CurrentConnection::start,
CurrentConnection::suspend, CurrentConnection::resume, CurrentConnection::end |
A xa_ calls returns an XAER_RB error code. |
| TRANSACTION_ROLLEDBACK | (TBD) | CurrentConnection::start,
CurrentConnection::suspend, CurrentConnection::resume, CurrentConnection::end |
A xa_ calls returns XAER_NOTA |
| TRANSACTION_ROLLEDBACK | (TBD) | CurrentConnection::end | In some circumstances, the XA integration may defer the rollback of a transaction branch until an association with this branch is ended. When this occurs and end is called with success set to TRUE, end raises TRANSACTION_ROLLEDBACK with the (TBD -- deferred rollback) minor code. |
| OTS/XA integration | JTA |
| CurrentConnection::start(Coordinator, otid_t) | Transaction.enlistResource(XAResource) |
| CurrentConnection::suspend(Coordinator, otid_t) | Transaction.delistResource(XAResource, TMSUSPEND) |
| CurrentConnection::resume(Coordinator, otid_t) | Transaction.enlistResource(XAResource) |
| CurrentConnection::end(Coordinator, otid_t, boolean) | Transaction.delistResource(XAResource, TMSUCCESS)
or Transaction.delistResource(XAResource, TMFAIL) |
| ResourceManager | no equivalent |
| BeforeCompletionCallback | no equivalent |
| CurrentConnection | no equivalent, although the notion of XA connection is the same in the OTS/XA integration and in JTA. |
| Connector | no equivalent |
| Automatic association | no equivalent |
| XA | JTA |
| A xa_switch_t object | A transactional resource factory |
| An opened rmid | A XAResource object |
| xa_open_entry(char *, int, long) | no equivalent -- JTA does not standardize an API to open or close XA connections |
| xa_close_entry(char *, int, long) | no equivalent |
| xa_start_entry(XID *, int, long) | XAResource.start(Xid, int) |
| xa_end_entry(XID *, int, long) | XAResource.end(Xid, int) |
| xa_rollback_entry(XID *, int, long) | XAResource.rollback(Xid, int) |
| xa_prepare(XID *, int, long) | XAResource.prepare(Xid, int) |
| xa_commit(XID *, int, long) | XAResource.commit(Xid, int) |
| xa_recover(XID *, long, int, long) | XAResource.recover(int) |
| xa_forget(XID *, int, long) | XAResource.forget(Xid, int) |
| xa_complete(int *, int *, int, long); | no equivalent |
| no equivalent | XAResource.getTransactionTimeout() |
| no equivalent | XAResource.isSameRM(XAResource) |
| no equivalent | XAResource.setTransactionTimeout(int) |
| Transaction Service | JTA |
| Current | TransactionManager, UserTransaction |
| Synchronization | Synchronization |
| Control/Coordinator/Terminator | Transaction |
| Current::begin() | TransactionManager.begin(), UserTransaction.begin() |
| Current::commit() | TransactionManager.commit(), UserTransaction.commit() |
| Current::rollback() | TransactionManager.rollback(), UserTransaction.rollback() |
| Current::rollback_only() | TransactionManager.setRollbackOnly(), UserTransaction.setRollbackOnly() |
| Current::get_status() | TransactionManager.getStatus(), UserTransaction.getStatus() |
| Current::get_transaction_name() | TransactionManager.toString(), UserTransaction.toString() |
| Current::set_timeout() | TransactionManager.setTransactionTimeout() |
| Current::get_control() | TransactionManager.getTransaction() |
| Current::suspend() | TransactionManager.suspend() |
| Current::resume() | TransactionManager.resume() |
| Coordinator::get_status() | Transaction.getStatus() |
| Coordinator::is_same_transaction() | Transaction.equals() ? |
| Coordinator::hash_transaction() | Transaction.hashCode() ? |
| Coordinator::register_synchronization() | Transaction.registerSynchronization() |
| Coordinator::rollback_only() | Transaction.setRollbackOnly() |
| Terminator::commit() | Transaction.commit() |
| Terminator::rollback() | Transaction.rollback() |
| Other Coordinator operations | no equivalent |
#include <CosTransactions.idl>
#pragma prefix "omg.org"
module XA
{
native XASwitch;
typedef short ThreadModel;
const ThreadModel PROCESS = 0;
const ThreadModel THREAD = 1;
local interface CurrentConnection
{
void
start(
// xa_start(TMNOFLAGS) or xa_start(TMJOIN)
in CosTransactions::Coordinator tx,
in CosTransactions::otid_t otid
);
void
suspend(
// xa_end(TMSUSPEND)
in CosTransactions::Coordinator tx,
in CosTransactions::otid_t otid
);
void resume(
// xa_start(TMRESUME)
in CosTransactions::Coordinator tx,
in CosTransactions::otid_t otid
);
void end(
// xa_end(TMSUCCESS) or xa_end(TMFAIL)
in CosTransactions::Coordinator tx,
in CosTransactions::otid_t otid,
in boolean
success
);
ThreadModel thread_model();
long
rmid();
};
interface BeforeCompletionCallback
{
void
before_completion(
in CosTransactions::Coordinator tx,
in CosTransactions::otid_t otid,
in boolean
success
);
};
interface ResourceManager
{
unsigned long
register_before_completion_callback(in
BeforeCompletionCallback bcc);
void
unregister_before_completion_callback(in
unsigned long key);
};
local interface Connector
{
ResourceManager
create_resource_manager(
in string
resource_manager_name,
in XASwitch
xa_switch,
in string
open_string,
in string
close_string,
in ThreadModel
thread_model,
in boolean
automatic_association,
in boolean
dynamic_registration_optimization,
out CurrentConnection
current_connection
);
CurrentConnection
connect_to_resource_manager(
in ResourceManager
rm,
in XASwitch
xa_switch,
in string
open_string,
in string
close_string,
in ThreadModel
thread_model,
in boolean
automatic_association,
in boolean
dynamic_registration_optimization
);
};
};
#endif /*!_XA_IDL_*/
Footnotes:
[1] This integration with C XA resource managers is briefly described in Sun's Java Transaction Service specification (http://java.sun.com/products/jts), "3.3 Support for pre-JTA Resource Managers".
[2] 0 is reserved for OSI CCR naming. -1 means the XID is null.
[3] Mappings for other languages that interface
with C, for example Ada and Java, could be defined as well.
For Java, it is expected that JTA-compliant XAResource implementations
will be much more common than C xa_switch_t structs wrapped using JNI.