Tag Archives: JTA

Bitronix configuration

After a first attempt to configure the JBoss transaction manager including  JMS, I finally decided to try the Bitronix transaction manager.

Here is the detail of the configuration.

First of all, you need to add the bitronix jars to your project, using Maven, it’s done very easily (unlike JBossTS, which needs a lot of different dependencies):

<dependency>
    <groupId>org.codehaus.btm</groupId>
    <artifactId>btm</artifactId>
    <version>2.1.3</version>
</dependency>

Now, the configuration, using Spring, as usual (you can retrieve some elements I used with the Narayana configuration:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

    <!-- enable transaction demarcation with annotations -->
    <tx:annotation-driven/>
    <tx:jta-transaction-manager/> 

    <!--  Bitronix Transaction Manager embedded configuration -->
    <bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices">
        <property name="serverId" value="spring-btm" />
        <property name="DefaultTransactionTimeout" value="300" />
    </bean>

    <!-- create BTM transaction manager -->
    <bean id="bitronixTransactionManager" factory-method="getTransactionManager"
        class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig" destroy-method="shutdown" />

    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" >
        <property name="transactionManager" ref="bitronixTransactionManager"/>
        <property name="userTransaction" ref="bitronixTransactionManager"/>
    </bean>

<!-- Datasource config -->
    <bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close">
        <property name="className" value="com.informix.jdbcx.IfxXADataSource" />
        <property name="uniqueName" value="expElecCliCoredb" />
        <property name="maxPoolSize" value="5" />
        <property name="driverProperties">
            <props>
                <prop key="user">${user}</prop>
                <prop key="password">${pwd}</prop>
                <prop key="serverName">${serverName}</prop>
                <prop key="databaseName">${database}</prop>
                <prop key="portNumber">${port}</prop>
                <prop key="ifxIFXHOST">${host}</prop>
                <prop key="ifxIFX_LOCK_MODE_WAIT">5</prop>
                <prop key="ifxIFX_XASPEC">Y</prop>
                <prop key="ifxDB_LOCALE">en_us.819</prop>
            </props>
        </property>
    </bean>

    <!-- XA JMS Connection Factory, ensure that the jms server has been yet created (with depends-on or order definitions) 
    otherwise the factory is not available and you will receive an exception 
    (javax.jms.JMSException: error looking up wrapped XAConnectionFactory at /XAConnectionFactory)-->
    <bean id="xaJmsConnectionFactory" class="bitronix.tm.resource.jms.PoolingConnectionFactory"
        init-method="init" destroy-method="close">
        <property name="className" value="bitronix.tm.resource.jms.JndiXAConnectionFactory" />
                <property name="uniqueName" value="jmsConnectionFactory" />
        <property name="minPoolSize" value="10" />
        <property name="maxPoolSize" value="10" />
        <property name="driverProperties">
            <props>
                <prop key="name">/XAConnectionFactory</prop>
            </props>
        </property>
    </bean>

    <!--     The producer sends the messages to be processed on the queue -->
    <bean id="notificationsProducer" class="net.classnotfound.cli.core.service.component.NotificationsProducer">
        <property name="connectionFactory" ref="xaJmsConnectionFactory"/>
        <property name="notificationsQueue" ref="notificationsQueue"/>
    </bean>

    <bean id="notificationService" class="net.classnotfound.core.service.NotificationServiceImpl">
        <property name="authorMapper" ref="authorMapper"/>
        <property name="notificationsProducer" ref="notificationsProducer"/>
    </bean>
</beans>

Concerning this configuration, we can see that the way we configure the datasources is almost the same for database as for JMS. The tricky part comes from HornetQ which creates the JMS connection factory but we cannot access it as a standard Spring bean. It’s why we have to get it from JNDI, resulting in a different configuration using dedicated Bitronix component: bitronix.tm.resource.jms.JndiXAConnectionFactory.

This connection factory is now used by the JMS producer. When we send a JMS message, this call is proxied by Bironix to enlist this action in the transaction.

To start the transaction, we do it as usual using the well-known @Transactional annotation. Doing this, the transaction is automatically started, and when we send the JMS message, it is included in the transaction.

Here an example of its usage:

    public void sendNotification(final String message) throws Exception {
        notificationsQueueConnection = connectionFactory.createConnection();
        notificationsQueueSession = notificationsQueueConnection.createSession(true, 0);
        notificationsQueueProducer = notificationsQueueSession.createProducer(notificationsQueue);

        TextMessage textMessage = notificationsQueueSession.createTextMessage(message);
        notificationsQueueProducer.send(textMessage);
    }

It’s important to note that the session is created in the send method, because following the JMS specification, “Any call to the Connection.createSession method must take place within the transaction”. I tried to create the session in a @PostConstruct, there is no error, you can send your message but… Nothing happens 🙁

Done!

JBoss transaction manager (Non XA)

This is my configuration for using the transaction manager implementation provided by JBoss outside of a JEE container. This configuration works for database transaction but not for JMS transaction;
The configuration is done with Spring which delegates the transaction management to the JBoss implementation. Even if I used all the stuff to be able to manage XA transactions, it is not working with JMS. It seems that the messages are not included in the transaction and with the lack of clear documentation to properly configure it, I finally gave up and decided to use an alternative provider.
I present here the configuration to use the JBoss transaction manager I used for database transaction management:

<!-- enable transaction demarcation with annotations -->
<tx:annotation-driven/>
<tx:jta-transaction-manager/> 

<!--     Transactionmanager Config  (uses CORBA??) -->
<!--     <bean class="com.arjuna.ats.jta.TransactionManager" factory-method="transactionManager" id="arjunaTransactionManager"></bean> -->
<!--     <bean class="com.arjuna.ats.jta.UserTransaction" factory-method="userTransaction" id="arjunaUserTransaction"></bean> -->

<!--     Transactionmanager Config  (local, without CORBA??-again...) -->
<bean class="com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple" id="arjunaTransactionManager"></bean>
<bean class="com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple" id="arjunaUserTransaction"></bean>

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" >
    <property name="transactionManager" ref="arjunaTransactionManager"/>
    <property name="userTransaction" ref="arjunaUserTransaction"/>
</bean>

<!-- Datasource config (use XA!)-->
<bean id="ifxXADataSource" class="com.informix.jdbcx.IfxXADataSource">
    <property name="user" value="${user}" />
    <property name="password" value="${pwd}" />
    <property name="serverName" value="${serverName}" /> 
    <property name="databaseName" value="${dbName}" /> 
    <property name="portNumber" value="${port}" />
    <property name="ifxIFXHOST" value="${host}" /> 
    <property name="ifxIFX_LOCK_MODE_WAIT" value="5" /> 
    <property name="ifxIFX_XASPEC" value="Y" />
    <property name="ifxDB_LOCALE" value="en_us.819" />        
</bean>

<bean id="dsXAConnectionFactory" class="org.apache.commons.dbcp.managed.DataSourceXAConnectionFactory">
    <constructor-arg><ref bean="arjunaTransactionManager"></ref></constructor-arg>
    <constructor-arg><ref bean="ifxXADataSource"></ref></constructor-arg>
</bean>

<bean id="pool" class="org.apache.commons.pool.impl.GenericObjectPool">
    <property name="maxActive" value="20"/>
    <property name="minIdle" value="5"/>
</bean>

<bean id="poolableConnectionFactory" class="org.apache.commons.dbcp.PoolableConnectionFactory">
    <constructor-arg name="connFactory" ref="dsXAConnectionFactory"/>
    <constructor-arg name="pool" ref="pool"/>
    <constructor-arg name="stmtPoolFactory"><null></null></constructor-arg>
    <constructor-arg name="validationQuery"><null></null></constructor-arg>
    <constructor-arg name="defaultReadOnly" value="false"/>
    <constructor-arg name="defaultAutoCommit" value="false"/>
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.managed.ManagedDataSource" depends-on="poolableConnectionFactory">
    <constructor-arg name="pool" ref="pool"/>
    <constructor-arg name="transactionRegistry" value="#{dsXAConnectionFactory.getTransactionRegistry()}"/>
</bean>

Maven dependencies:

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.framework.version>4.0.0.RELEASE</spring.framework.version>
    <apache.tomcat.version>7.0.50</apache.tomcat.version>
</properties>

<dependencies>
    <!-- Logging dependencies -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.6.1</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.6.1</version>
    </dependency>
    <dependency>
        <groupId>ant</groupId>
        <artifactId>ant-apache-log4j</artifactId>
        <version>1.6.5</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.8.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>com.informix.jdbc</groupId>
        <artifactId>ifxjdbc</artifactId>
        <version>4.10</version>
    </dependency>
    <dependency>
        <groupId>com.informix.jdbc</groupId>
        <artifactId>ifxjdbcx</artifactId>
        <version>4.10</version>
    </dependency>
    <dependency>
        <groupId>org.jboss.jbossts</groupId>
        <artifactId>narayana-all</artifactId>
        <version>4.17.16.Final</version>
        <type>pom</type>
    </dependency>
    <dependency>
        <groupId>org.jboss.spec.javax.transaction</groupId>
        <artifactId>jboss-transaction-api_1.1_spec</artifactId>
        <version>1.0.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.jboss.jbossts.jts</groupId>
        <artifactId>jbossjts-jacorb</artifactId>
        <version>4.17.13.Final</version>
    </dependency>
    <dependency>
        <groupId>org.jboss.logging</groupId>
        <artifactId>jboss-logging</artifactId>
        <version>3.1.0.GA</version>
    </dependency>
    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
    </dependency>
</dependencies>