java: Dropping the prefix

(Usually it’s good to drop one’s pretensions, but since all we have handy is a prefix we’ll just drop that instead…)

We are (for the moment, anyway) going to need to change our JBoss JDBC datasources so that the datasource resides in the global JNDI namespace, to be looked up under jndi-name Blah rather than java:Blah.  The reason we need to make this change is that Bitronix doesn’t currently support the java: prefix.

I seem to remember seeing a tag that you could put in your datasource configuration file that would tell JBoss to make the datasource available from the global JNDI namespace instead of from the JVM-specific java: one.

Ah yes, the JBoss 4 Application Server Guide and the DTD both document the use-java-context element.  Set that to false and it goes in the global JNDI namespace.  Using the java: namespace would be preferred; The JBoss 4 Application Server Guide also says,

DataSource wrappers are not usable outside of the server VM, so they are normally bound under the java:/, which isn’t shared outside the local VM.

That’s what we’ll need to set in our production datasources so that Bitronix Transaction Manager can exactly replicate the JNDI functionality at integration test time.

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.