Pramati Technologies Pramati Server XA Implementation and Configuration

Pramati Server XA Implementation and Configuration

Introduction

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:

  1. Updating multiple resources at once, within a global transaction context.
  2. ACID principles.

Working of 2-Phase Commit

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:

  1. start (Xid) - Enlist the resource as part of the transaction.
  2. end (Xid) - Inform the resource that the transaction is terminated.
  3. prepare (Xid) - Prepare for committing.
  4. commit (Xid) - Commit transaction.

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.

Server XA Implementation

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:

  1. JMS XAResource
  2. JDBC XAResource
  3. Connector XAResource

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.

Behavior of Pramati EJB Container

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.

Database Schema

XA implementation uses three tables: messages, subscribers and pending_messages and xa_messages. Following sections give a brief description of each table.

Messages table

This table contains information about president messages. The columns are:

Subscribers

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:

Pending_Messages

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:

XA_Messages

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:

Usage Scenarios

  1. MDB - Client need not use the XA semantics in its code. In case of MDB, user just needs to provide a class implementing javax.ejb.MessageDrivenContext and javax.jms.MessageListener interfaces. A XAConnectionFactory is used in case of MDBs. Transaction attribute for the onMessage() method of MDB is specified in the ejb-jar.xml and the destination mapping for the MDB is specified in the pramati-j2ee-server.xml in the following way:
<destination-mapping>
<destination-link>
jmsadaptorname#JMSQueue
</destination-link>
<conn-factory>
jmsadaptorname#JMSXAQueueConnectionFactory
</conn-factory>
<destination-user-name>
root
</destination-user-name>
<destination-password>cHJhbWF0aQ==</destination-password>
</destination-mapping>

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.

  1. Sending or receiving through a Session Bean - In this case, user must use Xconnection factory to execute the sending or receiving in a 2PC transaction. The resource reference for the connection factory will be of type javax.jms.QueueConnectionFactory, or javax.jms.TopicConnectionFactory. But the mapping in the pramati-j2ee-server.xml must be done to the corresponding XA factory.

i.e. ejb-jar.xml will have:

<resource-ref>
<description>No Description</description>
<res-ref-name>jms/JMSQueueConnectionFactory</res-ref-name>
<res-type>javax.jms.QueueConnectionFactory</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

while the mapping in the pramati-j2ee-server.xml should look like

<resource-mapping>
<resource-name>jms/JMSQueueConnectionFactory</resource-name>
<resource-link>
jmsadaptorname#JMSXAQueueConnectionFactory
</resource-link>
</resource-mapping>

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.

Adding XA Connection Factory from Shell

XAQueueConnectionFactory and XATopicConnectionFactory can be added using the default JMS admin shell.

Adding an XAQueueConnectionFactory

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:

jmsadmin@default:> add xaqcf myqueueconnectionfactory
Adding an XATopicConnectionFactory

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:

jmsadmin@default:> add xatcf myTopicconnectionfactory

To list all the connection factories configured on the server, type:

jmsadmin@default:>list factories
Topic Connection Factories: JMSTopicConnectionFactory
Queue Connection Factories: JMSQueueConnectionFactory
XA Topic Connection Factories: JMSXATopicConnectionFactory
XA Queue Connection Factories: JMSXAQueueConnectionFactory

This command displays all the configured connection factories on the server.

Adding a JDBC Connection Factory using Console

To add a Connection Factory, click `Add' under the JDBC Connection Factory section, and specify the following information:

Table 1: Details needed to add a JDBC Connection Factory
Fields
Options
Description
Driver / XA Datasource
 
An XA datasource provides connections that can participate in distributed transactions. Selecting a driver type automatically provides values for the Class and the Connection URL fields.
 
Oracle DB
For details about the drivers used, refer to Table 3 below.
 
SQL DB
For details about the drivers used, refer to Table 3 below.
 
XA datasource implementation
For XA data resources, the vendor provides the class that implements javax.sql.XADataSource interface. JDBC vendors that support distributed transactions must implement these interfaces.
If Database is IBM Informix, enter Class as com.informix.jdbcx.IfxXADataSource. If Database is Oracle, enter Class as oracle.jdbc.xa.client.OracleXADataSource
 
Others
You can use other drivers, or other XA datasource implementations. You must provide appropriate values for the Class and the Connection URL fields.
Class
 
JDBC driver or the XA datasource implementation class name is mandatory and is provided by the database vendor. It is used to access the database. Pramati Server supports JDBC 2.0 Drivers.
Selecting the driver in the field above automatically displays the associated class.
Connection URL
 
Refers to the location. If Database is Cloudscape, type URL as jdbc:cloudscape:<db_location>. If Database is Oracle, type URL as jdbc:oracle:thin:@<db_server:port>.
Selecting the driver in the field above automatically displays the associated Connection URL.
Name
 
Clients locate a resource by using the JNDI name. It is used as a reference to obtain all information about the resource and is unique in the server name space. Connection Factory JNDI name is mandatory.
Classpath
 
This option allows you to specify the location of the driver/implementation classes or jars. So different versions of the same driver can be used in the same instance of a running server.
For example, you can create a JDBC Connection Factory with OracleDriver version 3, and create another one with version 4. This is possible since the driver jars are not picked up from a common location, but are taken from the path specified in the input box.
In case the user does not specify the path, the server attempts to pick them up from the /lib/ext directory under the server installation directory, or from the system classpath.
Authorized by
 
Provides the authentication required to access the database. The application or container can provide authentication information. The authorization mode can be predefined.
 
Application
This field does not require the user to supply user name and password. They will be provided by the application while connecting to the Connection Factory.
 
Container
This field requires the user to supply user name and password at the time of creation.
 
 
User name: required to connect to the database
 
 
Password: required to authenticate access

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.

Table 2: Drivers supported by Pramati Server
Name of Database
Name of Driver
Oracle database
oracle.jdbc.driver.OracleDriver
Merant's Oracle driver
com.merant.datadirect.jdbc.oracle.OracleDriver
Informix JDBC driver
com.informix.jdbc.IfxDriver
Merant's Informix JDBC driver
com.merant.datadirect.jdbc.informix.InformixDriver
Cloudscape Embedded driver
COM.cloudscape.core.JDBCDriver
Cloudscape RMI driver
COM.cloudscape.core.RmiJdbcDriver
Merant's SQL Server Driver
com.merant.datadirect.jdbc.sqlserver.SQLServerDriver
JTurbo driver for SQL Server
com.ashna.jturbo.driver.Driver
MySQL JDBC Driver
com.mysql.jdbc.Driver

In case you select XA datasource as the driver, you will need to specify the following properties:

Table 3: Properties to be specified for XA datasources
Fields
Description
JNDI Name
The JNDI name specified earlier is displayed here. This is not editable.
Class Name
The class name specified earlier is displayed here. This is not editable.
Properties
Other optional properties required by the driver, or XA datasource are displayed here. For every XA datasource, there are nine standard properties.
Property Type
This field displays the property type. This is not editable.
Property Value
The value of the property is to be specified in this field.

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.

Editing a 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.

Deleting a JDBC Connection Factory

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.

Configuring XADatasource for J2EE Applications

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.

Configuring the persistent store

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:

<db-params>
<data-source-name>jmsdb</data-source-name>
<message-table>messages</message-table>
<subscriber-table>subscribers</subscriber-table>
<subscriber-msg-table>pending_messages</subscriber-msg-table>
<xa-table>xa_messages</xa-table>
</db-params>

Setting Transaction timeout

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:

  1. Start and connect to the Server. The Management Console page appears.
  2. Select Configure > Transactions in the Explore panel. The Transaction page appears.
  3. Specify the time duration (TimeOut) after which the transaction stops. This value is in seconds.
  4. Click Save.

All running transactions stop after this specified time and the transaction is rolled back.

Code Sample

The following code snippet provides configuration for XATopicConnectionFactory

(jms-config.xml)

<xa-topic-connection-factory>
<name>XATopicConnectionFactory</name>
<transport-type>rmi-transport</transport-type>
<naming-provider-url>
rmi://192.168.1.29:2001,rmi://192.168.1.29:2002
</naming-provider-url>
<is-secure>false</is-secure>
<enable-compression>false</enable-compression>
<compression-trigger-size-kb>10</compression-trigger-size-kb>
<max-unacknowledged-messages>50</max-unacknowledged-messages>
<auto-ack-server>true</auto-ack-server>
<bulk-send-enabled>false</bulk-send-enabled>
<bulk-send-rate>10</bulk-send-rate>
<default-message-priority>-1</default-message-priority>
<default-message-timetolive>-1</default-message-timetolive>
<default-message-delivery-mode>-1</default-message-delivery-mode>
</xa-topic-connection-factory>

FAQs

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

-Dcom.pramati.xajmsondirectlookup=true

Also use the normal API instead of XA API even in this case where you are using an XAConnectionFactory.

EntityLocalHome entityLocalHome = (EntityLocalHome)
lookup("java:comp/env/ejb/Entity", EntityLocalHome.class);
entityLocalHome.create("myaddr", new BigDecimal(10), "cust");
QueueConnectionFactory factXaQueueConnectionFactory =
(QueueConnectionFactory) lookup("java:comp/env/jms/fact",
QueueConnectionFactory.class);
Queue jmsQueue = (Queue) lookup("java:comp/env/jms/JMSQueue",
Queue.class);
QueueConnection conn =
factXaQueueConnectionFactory.createQueueConnection();
QueueSession sess = conn.createQueueSession(true,
Session.AUTO_ACKNOWLEDGE);
QueueSender sender = sess.createSender(jmsQueue);
TextMessage msg = sess.createTextMessage();
msg.setText("10");
sender.send(msg);


Pramati Technologies  © Copyright

 Pramati Server XA Implementation and Configuration