Monday, January 9, 2012

How to use JBoss Transactions in Spring

JBoss transactions could used in a Spring application for various reasons. Here are some scenarios:

1. Your standalone Spring application uses multiple resources and you would like to make multiple phase transaction commits
2. Your are using Hibernate or some other DAO framework to access multiple databases in your Spring application and this application is being deployed in Tomcat or some other servlet/JSP container where you need a transaction manager
3. You just need a JTA transaction provider in Spring for some other framework or library such as JBoss's Infinispan

Step 1
Download JBossTM or add dependency to your maven project as follows.
.
.
<dependency>
  <groupId>org.jboss.jbossts</groupId>
  <artifactId>jbossjta</artifactId>
  <version>4.16.0.Final</version>
</dependency>
.
.

If this is a web application, you must remove servlet jar from the dependency as follows:
.
.
<dependency>
  <groupId>org.jboss.jbossts</groupId>
  <artifactId>jbossjta</artifactId>
  <version>4.16.0.Final</version>
   <exclusions>
     <exclusion>
       <artifactId>jboss-servlet-api_3.0_spec</artifactId>
       <groupId>org.jboss.spec.javax.servlet</groupId>
     </exclusion>
   </exclusions>
</dependency>
.
.

Step 2
JBossTM could be configured for timeout and transaction object store folders. These settings could be done via setting system variables in Java command.
.
.
java -Dcom.arjuna.ats.arjuna.coordinator.defaultTimeout=60 
-Dcom.arjuna.ats.arjuna.objectstore.objectStoreDir=/tmp/jbosstm/tx/a 
-DObjectStoreEnvironmentBean.objectStoreDir=/tmp/jbosstm/tx/a ...
.
.

Or through Spring's system properties in application context file.

.
.
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" id="systemPrereqs">
  
  
  
  
    60
    /tmp/jbosstm/tx/a
    /tmp/jbosstm/tx/a 
       
  

</bean>
.
.

Step 3
Configure JBoss's Transaction Manager and User Transaction beans in Spring's application context as follows.
.
.
  
  
.

After this step, JBoss TM is up and running. If you like, you can use the transaction manager using the usual JNDI lookup.

Step 4
Finally, use JBossTM beans to configure Spring JTA transaction manager bean as follows.
.
.

  
    
  
  
    
  

.

This JTA transaction manager could now be used in your session factories or simply enable annotated driver transaction for auto injection.
.
.
<tx:annotation-driven></tx:annotation-driven>
.

Download Project
You can download my project from google code. SVN URL is http://ingenious-camel.googlecode.com/svn/trunk/SpringJBossTx

7 comments:

  1. Hi, how do you create spring beans that connect xadatasource or xaconnectionfactory (jms) to Jboss transactions ? Atomikos has "com.atomikos.jdbc.AtomikosDataSourceBean", Bitronix has something similar. Which is the equivalent here? IronJacamar? By any chance do you have any example? Thanks!

    ReplyDelete
  2. Hi Luciano, There is no direct support for xadatasource in Spring/JbossTM. You'll have to implement Jboss's dynamic class instantiation using the required XA driver and use JBoss's transaction driver (com.arjuna.ats.jdbc.TransactionalDriver) to instantiate the datasource.

    In this case, I would rather use a JEE server and avoid all the above complicated configurations.

    ReplyDelete
  3. It didn't work on pure Tomcat with additional libraries from JBoss.
    You probably forgot write about details added to server.xml,context.xml and web.xml files.

    ReplyDelete
  4. Hi - Adding JBoss libraries and modifying tomcat configuration is not such a good idea. Tomcat base should remain untouched and your application should have all the required libraries and configurations to run these kind of services. I guess you are facing problems due to servlet jar files in JBoss libraries. I have updated the maven config to tackle this issue and also attached a working project example. Just drop the built war in a clean tomcat 7 webapps folder and try URL http://localhost:8080/SpringJBossTx . Here is my tomcat log output:

    .
    .
    Jan 1, 2013 1:38:01 PM org.springframework.transaction.jta.JtaTransactionManager checkUserTransactionAndTransactionManager
    INFO: Using JTA UserTransaction: Transaction: unknown
    Jan 1, 2013 1:38:01 PM org.springframework.transaction.jta.JtaTransactionManager checkUserTransactionAndTransactionManager
    INFO: Using JTA TransactionManager: Transaction: unknown
    .
    .
    .
    Jan 1, 2013 1:38:22 PM com.arjuna.ats.arjuna.recovery.TransactionStatusManager addService
    INFO: ARJUNA012163: Starting service com.arjuna.ats.arjuna.recovery.ActionStatusService on port 65149
    Jan 1, 2013 1:38:22 PM com.arjuna.ats.internal.arjuna.recovery.TransactionStatusManagerItem
    INFO: ARJUNA012337: TransactionStatusManagerItem host: 127.0.0.1 port: 65149
    Jan 1, 2013 1:38:22 PM com.arjuna.ats.arjuna.recovery.TransactionStatusManager start
    INFO: ARJUNA012170: TransactionStatusManager started on port 65149 and host 127.0.0.1 with service com.arjuna.ats.arjuna.recovery.ActionStatusService
    .
    .

    ReplyDelete
  5. Step2 spring XML has incorrect nesting. All 3 should be at the top level, not nested inside each other.


    I am also trying to configure the values like:

    ${com.arjuna.ats.arjuna.objectstore.objectStoreDir}

    Where I have:



    And inside application.properties I have:

    com.arjuna.ats.arjuna.objectstore.objectStoreDir=${java.io.tmpdir}/jbosstm/a

    I am sure you can guess what I am trying to achieve with this config, I want it to just-work(tm) without the headache of configuring up an absolute path to be able to deploy it. I am not sure what system is in charge or interpolating the ${java.io.tmpdir} value.

    Does anyone have a reference handy to other ways to configure JBoss TM (that don't require messing with SystemProperties which as not so useful when you might want multi-tennancy in the future). Then maybe I would want to configure: ${java.io.tmpdir}/${servletContextPath}/jbosstm/a



    ReplyDelete
  6. Would be useful to provide information on how to debug JBoss TM (I turned on all I could and at TRACE level got just 5 log entries) with no idea why it can not made to work on a simple setup.

    I've kind of run out of time (for the moment) to keep looking towards JBoss TM as a JTA solution inside servlet container (where spring is also in use). Going to at least evaluate the alternatives with the same amount of time spent before coming back.

    I have got as far as what your blog provides but it is not a usable example, just an example of how to configure spring in XML towards using JBoss TM. It certainly seems to create a TransactionManager and UserTransaction object but when you try to use the object to start a transaction you get a null back (from TransactionImple.getTransaction()), no exception and no error indicated. Your SVN example does not even use any JDBC it kind of is a transaction all in memory and not that useful to learn anything from.

    Maybe it would be better to provide a full working project demonstrating at least 2 JDBC data sources in use with rollback and commit working. Ideally turn off all other complex features JBoss TM provides (cross JVM / cross network, recovery, ORB/CORBA/WS, etc...) just a simple JTA implementation designed for a single JVM able to handle multiple JDBC connections.

    My view is that any smaller setup than this would not consider JTA anyway (if it was effort to implement it) as other options are available to cover those use cases.

    ReplyDelete
  7. Hi Darryl - I am sorry you are facing these issues. I am currently using JBoss TM with two JDBC datasources and Apache Camel JMS with no issues. Here are my configs running in tomcat 7:

    applicationContext.xml
    ----------------------
    <!-- ABC hibernate -->
    <bean id="abcDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
      <property name="jndiName" value="java:comp/env/jdbc/abc"/>
    </bean>

    <bean id="abcSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
      <property name="dataSource" ref="abcDataSource" />
    </bean>

    <!-- XYZ hibernate -->
    <bean id="xyzDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
      <property name="jndiName" value="java:comp/env/jdbc/xyz"/>
    </bean>

    <bean id="xyzSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
      <property name="dataSource" ref="xyzDataSource" />
    </bean>

    <bean id="arjunaTransactionManager" class="com.arjuna.ats.jta.TransactionManager" factory-method="transactionManager" />
    <bean id="arjunaUserTransaction" class="com.arjuna.ats.jta.UserTransaction" factory-method="userTransaction" />

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

    <tx:annotation-driven />

    And I am using spring transaction by declaring semantics in the class file.

    Dao Class
    ---------
    @Repository
    @Transactional
    public class EmployeeDaoImpl implements EmployeeDao {
      @Autowired
      @Qualifier(value="abcSessionFactory")
      private SessionFactory abcSessionFactory;

      @Autowired
      @Qualifier(value="xyzSessionFactory")
      private SessionFactory xyzSessionFactory;

      @Override
      @Transactional
      public Employee getEmployee(String employeeId) {

        Session abcSession = abcSessionFactory.getCurrentSession();
        Session xyzSession = xyzSessionFactory.getCurrentSession();
        .
        .
        .
    }

    http://static.springsource.org/spring/docs/3.0.x/reference/transaction.html#transaction-declarative-annotations

    ReplyDelete