XA, JNDI and Bitronix, part 2: JDBC

In our last episode, we worked to get the JMS connection factory able to be looked up via JNDI using the Bitronix Transaction Manager… and after fixing several things, God helping us, we succeeded.  Today we want to re-enable the rest of the Spring beans in our project (Hibernate beans, default message listener container, etc.) and see them all working with Bitronix.

Updating to BTM 1.3

Over the weekend, Bitronix Transaction Manager version 1.3 final was released.  I wanted to update to that from version 1.3-RC2, so I updated the <version> tag of the btm artifact in my project’s pom file and removed the <repository> section that had been pointing to the codehaus-snapshots repository (the full 1.3 release is now on the regular Maven 2 central repository, so I no longer need to reference the early releases repository).

I re-ran my integration test and it still does the JNDI lookup fine.  That was easy!

Bringing In the Beans

Our goal is to not have to modify production Spring bean files for integration testing… but this project will help us determine how to set up the production bean files to make this possible.  So this time around, I’m going to have to modify my mock-production bean files to work in the non-app-server environment

First, let’s get rid of the JTA transaction manager bean ( <tx:jta-transaction-manager /> ) from our main bean file.  The definition of the transactionManager bean needs to be different in a production environment than with Bitronix, so that bean doesn’t belong in a production beans file.  Along with this, we need to rename our transaction manager bean from JtaTransactionManager to transactionManager, since that’s what the jta-transaction-manager bean provided and the Hibernate sessionFactory is expecting.

We also need to remove the “java:” from the jndi-name our queueConnectionFactory expects to look up: <jee:jndi-lookup id="queueConnectionFactory" jndi-name="java:activemq/QueueConnectionFactory" />

…and similar with the Oracle datasource in spring-beans-hibernate-SimplePersonDAO.xml:     <jee:jndi-lookup id="myDataSource" jndi-name="java:XAOracleDS" />

Can’t Find the Database Datasource

Now when we run our integration test, Spring complains that nothing named XAOracleDS is available from JNDI.  Ah, yes — we need to give Bitronix the details of what’s needed so it can make a datasource available under that name.

So, combining step 2 of the Spring procedure with some hints from the JDBC pools setup documentation, I cobbled together this bean:


    <bean id="oracleDataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close">
<property name="className" value="oracle.jdbc.xa.client.OracleXADataSource" />
<property name="uniqueName" value="XAOracleDS" />
<property name="maxPoolSize" value="5" />
<property name="driverProperties">
<props>
<prop key="user">myUser</prop>
<prop key="password">myPassword</prop>
<prop key="URL">jdbc:oracle:thin:@localhost:1521:XE</prop>
            </props>
        </property>
    </bean>

I don’t expect this to work right off, because I don’t think I have the Oracle XE library listed in my pom file.  But — vaguely TDD-style — let’s try it first and verify that we do get an error.  Sure enough:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘oracleDataSource’ defined in class path resource [spring-beans-bitronix.xml]: Invocation of init method failed; nested exception is bitronix.tm.resource.ResourceConfigurationException: cannot create JDBC datasource named XAOracleDS

And way down toward the end of the stack trace is this:

Caused by: java.lang.ClassNotFoundException: oracle.jdbc.xa.client.OracleXADataSource

Let’s add the Oracle jdbc driver dependency to our pom file:


        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc5</artifactId>
            <version>11.1.0.6.0</version>
        </dependency>

Bitronix Transaction Manager Refuses to Start?

After Maven Eclipsing to update my project with the new dependency, I now get this:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘BitronixTransactionManager’ defined in class path resource [spring-beans-bitronix.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public static synchronized bitronix.tm.BitronixTransactionManager bitronix.tm.TransactionManagerServices.getTransactionManager()] threw exception; nested exception is bitronix.tm.utils.InitializationException: recovery failed, cannot safely start the transaction manager

Why can BTM not safely start?  Is it trying to force me to come along with its view of how much risk is acceptable in a system’s configuration?  …But wait, let’s look at some of those Caused-bys farther down in the stack trace:

Caused by: bitronix.tm.recovery.RecoveryException: error running recovery on resource XAOracleDS

And farther down:

Caused by: java.sql.SQLException: Io exception: The Network Adapter could not establish the connection

Aha — I forgot to fire up my local Oracle XE instance.  Oops!

Thank You for Watching the Caused-By Show

(Remember The Cosby Show? :)

After starting up Oracle and re-running my test… I get the same error again, but — now I’m getting some XA verbiage showing up in the Caused-Bys:

Caused by: bitronix.tm.recovery.RecoveryException: error running recovery on resource XAOracleDS (XAER_RMERR)

and

Caused by: javax.transaction.xa.XAException

I wonder if that means we’re getting close?

After messing around a while, I searched the BTM forum and found that the Oracle user under which I’m performing the integration test needs to have certain additional database privileges:

grant select on sys.dba_pending_transactions to myUser;
grant select on sys.pending_trans$ to myUser;
grant select on sys.dba_2pc_pending to myUser;
grant execute on sys.dbms_system to myUser;

[First, drop to Oracle SQL command line and do connect / as sysdba]

Hey, now the integration test passes!  (Granted, it’s only instantiating all the beans so far, but that means the Oracle JDBC JNDI lookup must have succeeded…!)

Guess allowLocalTransactions needs to stay

Following one of the examples, I had put the following two settings in my Oracle data source bean definition:

<property name="allowLocalTransactions" value="true" />
<property name="testQuery" value="SELECT 1 FROM DUAL" />

Do we really need these settings?  Let’s try removing them.

We get an error:

java.sql.SQLException: error enlisting a JdbcConnectionHandle of a JdbcPooledConnection from datasource XAOracleDS in state ACCESSIBLE wrapping oracle.jdbc.driver.T4CXAConnection@948069 on oracle.jdbc.driver.LogicalConnection@17a7706

…with this Caused-By:

Caused by: bitronix.tm.internal.BitronixSystemException: resource ‘XAOracleDS’ cannot be used outside XA transaction scope. Set allowLocalTransactions to true if you want to allow this and you know your resource supports this.

Removing the testQuery property setting doesn’t seem to have any similarly bad effect, but maybe I’ll just leave both of these settings in for the moment.

Where We Are

We now have all our beans instantiated, including the big three (JDBC datasource, JMS connection factory, and XA transaction manager), and the only changes we had to make to the “production” bean files were to remove the java: namespace from the jndi-names and get rid of the jta-transaction-manager bean declaration.

In our next installment, we’ll try sending a message and see what happens.

Advertisements

Getting the Oracle XA datasource in place

Last time, we seemed to see a successful rollback of one resource (the JMS message receive).  Today we want to try to get the JDBC datasource to participate in the transaction, to set up for demonstrating a rollback during a full two-phase commit.


I had an Oracle datasource configuration file sitting around waiting for when I was ready to test the Hibernate portion of my XA transaction, but yesterday I took a look at it and it looks like a non-XA datasource.  For one thing, the bulk of the content is within a  <local-tx-datasource> element.  For another, there is this comment near the top:

<!--| This datasource connects to oracle.  It is a LOCAL TRANSACTION data source (as opposed to
| an XA transaction datasource
|-->

I’ll need an XA datasource, though, not a local one…

An example to work from

I found Section 8.3.2. Installing the JDBC Driver and Deploying the DataSource in JBoss’s Getting Started with JBoss 4.0 guide, which has an example Oracle XA datasource.  I started with that example and made the following changes:

  • For the URL, I used jdbc:oracle:thin:@localhost:1521:XE
  • Different username/password — I followed the instructions (also in Section 8.3.2.) for creating a user via the Oracle SQL command line
  • Commented out the mbean element

(When I first deployed this datasource, I got a stacktrace containing the following error:

Caused by: java.lang.ClassNotFoundException: No ClassLoaders found for: org.jboss.resource.adapter.jdbc.xa.oracle.OracleXAExceptionFormatter

)

[Update 8/21/2008: I found instructions for fixing the missing ExceptionFormatter class so that this mbean no longer needs to be commented out: the class name in the code attribute needed to be corrected from org.jboss.resource.adapter.jdbc.xa.oracle.OracleXAExceptionFormatter to org.jboss.resource.adapter.jdbc.vendor.OracleXAExceptionFormatter.]

Now when I deploy I see:

09:29:09,112 INFO  [ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=DataSourceBinding,name=XAOracleDS' to JNDI name 'java:
XAOracleDS'

Added JNDI lookup to Spring bean file

I went to the Spring beans file for the Hibernate stuff and put this JNDI lookup there:

<jee:jndi-lookup id="myDataSource" jndi-name="java:XAOracleDS" />

So, now that will be there when I’m ready to try it out.

Update: Oracle JDBC driver

I was looking through some notes I had and another part of the setup, which I had done previously (besides installing Oracle Database 10g Express Edition), was to go in our Maven repository to the following location:

  • group id:  com.oracle
  • artifact id: ojdbc5
  • version: 11.1.0.6.0

and put the ojdbc5-11.1.0.6.0.jar from there in JBoss’s server\default\lib directory.

Next time we’ll configure the Hibernate session factory…