![]() |
Pramati Server XA Implementation and Configuration |
XA or distributed transaction semantics referred to as 2-Phase Commit (2PC) transactions let the clients update multiple resources in *sync* - and therefore commits/rollbacks will be done consistently for all resources involved in the transaction. For example, all resources will either be committed, or rolled back in coordination with the global transaction.
In case of JMS, all optional XA related interfaces of javax.jms package are implemented in Pramati Server. The primary focus is to provide an implementation that would be used by EJB Containers for distributed transactions involving JMS. This involves MDBs as well as other beans, which could obtain a JMS Session and send messages through it. The implementation is also generic enough for any application server to use as long as it has a Transaction Manager and is capable of handling all required transaction management semantic expected of the Application Server.
The XA implementation supports:
For a given XA resource, a JTA Transaction Manager will get a handle on the resource's XAResource object, and then call methods in following sequence:
The Xid parameter identifies the transaction. It is a unique key to the transaction from the XA resource's perspective. Xid comprises the global transaction ID and the branch qualifier. All XA resources for a given transaction will share the same global transaction ID; additionally, each resource will see its own branch qualifier, which is unique to the global transaction. For instance, if you have 3 XA resources involved in a global transaction, you will have 3 Xids, all with the same global transaction ID, but each with a different branch qualifier.
The start() and end() calls tell the resource when it is starting to get involved in a transaction, and when its involvement ends. The latter two calls - prepare() and commit() are the integral part of 2PC. Once all the work is done in a transaction, the Transaction Manager first calls all the resource managers with a prepare() call. At this point, all resources get to vote on the outcome of the transaction. If any resource votes no, the transaction rolls back. If they all vote yes, the transaction will be committed. If the latter occurs, the Transaction Manager then calls commit() on all of the resources.
Pramati Server's XA implementation overview is provided with the following figure:
All the usual JTA calls are given in dotted lines. The supported resources are:
Global transactions are not logged by default but can be enabled.
Pramati Message Server provides support for XA transactions through an XA-enabled driver. (For a list of compatible drivers, see the section below). The traditional mode of transaction fails in a distributed transaction scenario with transactions distributed among different resource managers such as databases. Pramati Message Server uses 2PC as a workaround to coordinate a transaction across one or more resource managers. Data integrity is guaranteed by ensuring that transactional changes are committed in all participating databases or completely rolled back from all databases, reverting to the original state.
Three possible transaction scenarios for the EJB container are:
In case of BMT, XAResource can not be enlisted within a transaction. The container gets the Session object from XASession and calls a commit() on it. This could be done immediately after invoking onMessage() on the MDB returns or at a later point such as- before returning the session to the pool. This means that the Session should be ready to acknowledge all the messages delivered to the session's MessageListener (MDBeanHandler) when this commit() is called.
It is a similar scenario with Container Managed Transaction with transaction attribute `NotSupported' but for one difference -the Session.commit() should be called before the onMessage() on MDBeanHandler returns. This implies that there will always be only one message in a session which has been delivered to its MessageListener but not acknowledged yet.
As far as the JMS provider is concerned, there is no difference between these two cases. In both cases, the Session's associated XAResource object does not become part of a distributed transaction and a regular Session.commit() is used to commit the transaction.
Things are a lot more complicated for Container Managed Transaction with transaction attribute `Required'. Before calling the onMessage() of MDB, the container will start a transaction, get the XAResource from the session and get the resource enlisted with the transaction. Within the onMessage() of MDB (i.e. bean provider's code), one could obtain one more session (a different session obtained through the container) and send some messages through that session. These sends also become part of the transaction. It is expected that the container will always get XASessions from the JMS Provider and will register XAResource of this XASession before returning the underlying Session to the bean. When transaction commits, which must happen before the onMessage() on MDBeanHandler returns, all this work should be committed.
If the send operation was done using the session obtained from the same RM instance, then the XAResource.start() for this XAResource will be done with TMJOIN flag. Now, if there are no other resource managers involved in the transactions, XAResource.commit() will be called with boolean onePhase true. Else, this variable will be false. If onePhase is false, the JMS Server should have received a prepare() call for this XID.
XA implementation uses three tables: messages, subscribers and pending_messages and xa_messages. Following sections give a brief description of each table.
This table contains information about president messages. The columns are:
This table contains a DSI vs. Subscriber ID map. For each new subscriber created, a new subscriber ID is allotted based the previous maximum ID. The columns are:
This table contains all the undelivered/unacknowledged messages for a particular durable subscriber. For each subscriber ID, there will be multiple entries corresponding to each message that is meant for this subscriber but hasn't been delivered yet or has been delivered but not acknowledged yet. The columns are:
This table need not be created if the JMS Server will not be used by any XA aware clients. All XA related calls will then throw an XAException. When present, this table will contain information for a transaction when it is prepared. For all sent messages and messages to be acknowledged as part of that transaction, there will be a row containing Xid, message ID and a flag indicating if the message was prepared for send or acknowledge. For message acknowledgements, additional information is also present in the same row - the client ID of the client acknowledging the message and Subscriber Name, if the consumer is a durable subscriber/durable connection consumer. The columns are:
where destination-link specifies the name of the JMS server and the name of the destination to be used for listening for messages, separated by #. conn-factory specifies the name of the JMS server and the name of the connection factory to be used for listening for messages, separated by #. destination-user-name and destination-password are the username and encrypted password for creation of the connection for receiving the messages.
If the transacted attribute for the MDB's onMessage() method is `Required', the message is received in a new transaction created at the beginning of onMessage() method. Else if the transaction attribute is `NotSupported', onMessage( )is executed in a non transacted mode.
i.e. ejb-jar.xml will have:
while the mapping in the pramati-j2ee-server.xml should look like
The method in which the send or receive operation is done may also contain a transaction. In that case JMS must participate in the incoming transaction. For that, other than using the XAConnectionFactory, user must not call commit on the JMSSession. In case of Container Managed Transaction session.commit() is called by the container when the global transaction is committed. In case of Bean Managed Transaction UserTransaction.commit() will commit the session. The JMS connection must be closed in both cases.
XAQueueConnectionFactory and XATopicConnectionFactory can be added using the default JMS admin shell.
To add an XAQueueConnectionFactory to the message server, type the command add xaqcf if you are using server command line or remote shell. This command takes a single argument <XAQconnectionFactoryName>. The name should be unique.
To create and add 'myqueueconnectionfactory' which implements javax.jms.XAQueueConnectionFactory, to the message server, type:
To add an XATopicConnectionFactory to the message server, type command add xatcf if you are using JMS server command line or remote shell. This command takes a single argument <XATconnectionFactoryName>. It should be unique.
To create and add 'mytopicconnectionfactory' which implements javax.jms.XATopicConnectionFactory on Pramati Message Server, type:
To list all the connection factories configured on the server, type:
This command displays all the configured connection factories on the server.
To add a Connection Factory, click `Add' under the JDBC Connection Factory section, and specify the following information:
When the URL provided is incorrect, a message "Network adaptor could not establish a connection" is displayed. Appropriate messages are displayed when the user name or password is incorrect.
Note: An XA datasource that has been authored by the application does not require user name and password.
In case you select XA datasource as the driver, you will need to specify the following properties:
The properties for an XA datasource may be standard or driver-specific. Depending on the driver selected earlier, the Console displays the standard properties as well as the proprietary properties accepted by the driver. Their values are optional.
Note: Certain vendor-specific properties may also need to be specified. These must be entered in the Property column.
For more information about the Oracle datasource class, visit http://technet.oracle.com/doc/oracle8i_816/java.816/a81354/connpoc1.htm. These properties follow the JavaBeans design pattern.
Clicking `Next>>' displays the Step 2 for creating the JDBC Connection Factory.
Using the `Properties' button against the JNDI Connection Factory Name displays the properties with which the connection factory was created. These include:
Whether you can modify the properties depends on the driver class selected.
Select the check box for the Connection Factory Resource to be deleted on the Resources page and click `Delete'. This deletes the selected Connection Factory Resource.
A datasource provides pooled connections to applications. The physical connections are obtained from a JDBC Connection Factory and pooled by the datasource. It also provides other configurable features such as connection validation, pool sizes, connection time out, and pool refresh interval.
A datasource on Server is always associated with a JDBC Connection Factory. Multiple datasources can be associated with the same JDBC Connection Factory.
The JDBC API supports two extensions of the datasource interface. These extensions are:
The X/Open CAE specification (XA) facilitates two-phase commit in transactions and forms a part of the JDBC 2.0 Standard Extension.
Before configuring a persistent store on the Management Console, make sure that the following tables exist in the DB:
To configure a persistent store on the Management Console,
1. Choose JMSServer > Persistent Store in the left panel.
2. The default Persistent Store maintained is jmsdb. This can be edited
3. To add a new Persistent Store, click `add' button in the right side panel.
4. Enter the following information:
5. Restart the server to get the new Persistent Store.
To set the database properties for embedded JMS, you need to edit jms_config.xml. A datasource named jmsdb must be present in the resource-config.xml.
This file is located at <install_dir>/server/nodes/<server name>/config/ and contains a <db-params> tag, which takes the database parameters. Following is the outline of this tag and options available:
To prevent bottlenecks when multiple transactions are running, you can specify the duration for which a transaction can run. After this time, a transaction timeout occurs and the transaction rolls back.
Note: Make sure that this value takes into account your system environment and business logic, otherwise, the transactions may stop before completion.
To specify the duration after which a transaction rollback occurs:
All running transactions stop after this specified time and the transaction is rolled back.
The following code snippet provides configuration for XATopicConnectionFactory
(jms-config.xml)
Q. All Pramati JMS connection factories are actually XA. Is the same true for JDBC data-sources, disregarding the fact what driver class is used?
Pramati treats all datasources as XA datasources. Most clients use regular non-XA datasources (the driver name + URL datasources). These datasources in the context of EJBs don't have transactions for Persistence and other operations. Hence the non XA datasources are "wrapped-up" as XA datasources.
Q. Can default JMS adapter for embedded JMS Server (with default server) work with non-xa connection factories? Even if I create connection factories of non-XA type, the server does not use them. It still uses the default XA types.
Pramati doesn't provide support for non-XA factories in Application Server. This is to enable any JMS send/receive from EJBs to become part of a distributed transaction.
By default four factories are pre-configured by Pramati installer -
A message is sent when database is updated. The DB & JMS being two independent resource managers, they need to participate in a distributed transaction coordinated by the Transaction Manager. This is possible only if XA JMS connection factories are used.
Still, when the developer is sure that a distributed transaction is not needed, it should be possible to use non-XA factories. The problem there is that it leaves enough room for the developer to write code which doesn't behave the way it's expected to, and is also tough to debug. As for providing both XA & Non-XA default factories on the JMS Server, please note that the JMS Server will also be used by stand alone java clients. These clients don't need to use XA factories at all, and should use Non-XA factories only.
Q. When should I use an XAConnectionFactory?
Any operations participating in transaction in the J2EE VM must use XAConnectionFactories. Also in case of direct lookup from the JMS Server, Pramati Server does not bind the wrappers on the J2EE nodes. Hence contextual lookup for factories as resource reference must be used if direct lookups of connection factories are being used.
If direct lookups of XA connection factories are being used please start the server with -D property
Also use the normal API instead of XA API even in this case where you are using an XAConnectionFactory.
Pramati Technologies © Copyright |
Pramati Server XA Implementation and Configuration |