XA, JNDI and Bitronix, part 1

For the past couple few days I’ve been working to update my XA example to use Bitronix Transaction Manager to do the JNDI lookup and transaction management so that my beans files can remain the same (or as much so as possible) for integration testing outside the app server.

The Process

Initial Steps

I followed the instructions for using pre-release versions of BTM with Maven and then went over to the Spring integration instructions:

  • Step 1, copying the jars, is taken care of by Maven;
  • For Step 2, I’m using an Apache Commons BasicDatasource;
  • For steps 3 and 4 I copied the bean definitions from the instructions (but taking out the depends-on attribute since that is supposed to have been fixed).

Trying it out

Let’s run the project as a JUnit test in Eclipse (at first I chuckled in disbelief that that the keyboard shortcut for this is Shift+Alt+X, t, but now it doesn’t seem so bad… ;)

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘queueConnectionFactory’: Invocation of init method failed; nested exception is javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file:  java.naming.factory.initial Related cause: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.apache.commons.dbcp.BasicDataSource] for bean with name ‘jdbcDataSource’ defined in class path resource [applicationContextBitronix.xml]; nested exception is java.lang.ClassNotFoundException: org.apache.commons.dbcp.BasicDataSource     …

So for starters, I don’t have a JNDI InitialContext, and we can’t find the org.apache.commons.dbcp.BasicDataSource class.

Fixing the DataSource

We’ll start with the BasicDataSource since that’s easy: I need to add that to the pom file:

		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.2.2</version>
			<scope>test</scope>
		</dependency>

Fixing the JNDI part

Ok, now when I run the project as a JUnit test again, that error went away, but I still get the JNDI NoInitialContextException.  To resolve this one, I’ll need to see how to use Bitronix’s new JNDI support.

Looking around on the Bitronix front page, The JndiXaConnectionFactory documentation is the only JNDI thing I see, and that doesn’t seem to be what I need… but a Google search (poor ask.com — the same search yielded almost no results there) points me to BTM’s Hibernate documentation.  In the section titled Resource Loader way: Creating the datasources, it has this nugget:

Resource Loader JNDI binding
When you configure the Resource Loader to bind datasources to JNDI (by setting the bitronix.tm.resource.bind property to true) it will bind them using the unique name as the JNDI location.

I think this is what we need.  Using the examples from the ResourceLoader page as a starting point, here is my jndi-bitronix.properties file, which I have, not knowing any better, placed in the root of my classpath in src/main/resources in my project:

bitronix.tm.resource.bind=true

resource.ds1.className = oracle.jdbc.xa.client.OracleXADataSource
resource.ds1.uniqueName = java:XAOracleDS
resource.ds1.maxPoolSize = 3
resource.ds1.driverProperties.url = jdbc:oracle:thin:@localhost:1521:XE
resource.ds1.driverProperties.user = myUserName
resource.ds1.driverProperties.password = myPassword

resource.mq1.className = org.activemq.ActiveMQXAConnectionFactory
resource.mq1.uniqueName = java:activemq/QueueConnectionFactory
resource.mq1.maxPoolSize = 2
#resource.mq1.driverProperties.userName = defaultUser
#resource.mq1.driverProperties.password = defaultPassword
resource.mq1.driverProperties.brokerURL = vm://localhost

And here is my new spring-beans-bitronix.xml Spring bean file:

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

	<bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices">
<property name="serverId" value="spring-btm" />
<property name="resourceConfigurationFilename" value="jndi-bitronix.properties" />
	</bean>

	<bean id="BitronixTransactionManager" factory-method="getTransactionManager" class="bitronix.tm.TransactionManagerServices"
		destroy-method="shutdown" />

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

Notice on line 9 that we’re pointing the configuration manager to the properties file.

We Fire Up the Test And…

Still a NoInitialContextException.  Come to think of it, I wonder if the order of initialization of these beanfiles is significant.  The <jee:jndi-lookup/> beans in my spring-beans-main.xml and spring-beans-hibernate-SimplePersonDAO.xml beans files would need the JNDI server to be up at the time they’re instantiated… let’s try putting the bitronix beans first in the load-em array and see if that makes a difference:


private static final String[] beanFiles = { "spring-beans-bitronix.xml", "spring-beans-main.xml", "com/example/db/spring-beans-hibernate-SimplePersonDAO.xml" };

//...

new ClassPathXmlApplicationContext(beanFiles);

A Different Error

Now we get this error:

Error creating bean with name ‘btmConfig’ defined in class path resource [spring-beans-bitronix.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: org/slf4j/impl/StaticLoggerBinder

I thought I had checked our project’s Referenced Libraries and seen the slf4j library in there (from some transitive dependency — I had not added slf4j to our project’s pom file).  Taking another look, I see that I only have slf4j-api.

Hmm, shouldn’t BTM be responsible for its own dependencies?  Looking at the BTM new user guide, I see that the jar is actually slf4j-jdk14-1.4.3.jar, and looking in BTM 1.3-RC2’s pom file, I see that the slf4j-jdk14 artifact is set to optional there:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.4.3</version>
<scope>runtime</scope>
<optional>true</optional>
</dependency>

From Maven’s POM Reference, if an artifact is marked optional in a Maven project X, that means that if my project depends on project X, I don’t need that dependency.  In this case, though, it seems that I do need the dependency — so I’ll add it to my pom file thusly:

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jdk14</artifactId>
            <version>1.4.3</version>
            <scope>runtime</scope>
        </dependency>

This seems like a bug in BTM’s pom file — seems like slf4j-jdk14 should not be marked optional if it’s actually needed by dependent projects (like mine).

Update 8/15/2008: It’s not a bug.

A First Peep from the BitronixTransactionManager

Hey, hey — this time when I run my project as a JUnit test, I get this in the console:

log4j:WARN No appenders could be found for logger (com.example.startup.StartupSvc).
log4j:WARN Please initialize the log4j system properly.
Aug 14, 2008 10:02:31 AM bitronix.tm.BitronixTransactionManager logVersion
INFO: Bitronix Transaction Manager version 1.3-RC2
Aug 14, 2008 10:02:31 AM bitronix.tm.Configuration buildServerIdArray
INFO: JVM unique ID:

I’ve been getting the log4j warnings since I haven’t made a log4j.properties… but look, BTM says hello!

We’re getting a new error, too:

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 java.lang.NullPointerException

Now, what could getTransactionManager need, that it’s getting a NullPointerException?

[Time passes, and when we rejoin our beleaguered hero…]

Since last time, I set up the log4j properties like this:

log4j.rootLogger=DEBUG, A1
log4j.logger.bitronix.tm=DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

I didn’t really see anything that helped though.  Looking at the bitronix.tm.Configuration documentation, I’m not sure that my attempt at setting the resourceConfigurationFilename is working.  Specifically, maybe line 3 of this bean definition is not working as I expect:

    <bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices">
<property name="serverId" value="spring-btm" />
<property name="resourceConfigurationFilename" value="/jndi-bitronix.properties" />
    </bean>

How else do we tell the Configuration singleton about the properties file, though?

Telling the Configuration singleton about the properties file

The properties file section of the documentation for bitronix.tm.Configuration says that if the properties file is named bitronix-default-config.properties at the root of the classpath, bitronix.tm.Configuration will magically find it.  Let’s try that:

Hey, the error goes away, and for the first time, the BitronixTransactionManager bean and the JtaTransactionManager beans initialize without errors!  Notice especially lines 12 and 13:

0    [main] INFO  com.example.startup.StartupSvc  - Starting up
79   [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext  - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@480457: display name [org.springframework.context.support.ClassPathXmlApplicationContext@480457]; startup date [Thu Aug 14 12:35:05 EDT 2008]; root of context hierarchy
188  [main] INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader  - Loading XML bean definitions from class path resource [spring-beans-bitronix.xml]
439  [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext  - Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext@480457]: org.springframework.beans.factory.support.DefaultListableBeanFactory@d19bc8
486  [main] INFO  org.springframework.beans.factory.support.DefaultListableBeanFactory  - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@d19bc8: defining beans [btmConfig,BitronixTransactionManager,JtaTransactionManager]; root of factory hierarchy
Aug 14, 2008 12:35:05 PM bitronix.tm.BitronixTransactionManager logVersion
INFO: Bitronix Transaction Manager version 1.3-RC2
Aug 14, 2008 12:35:05 PM bitronix.tm.Configuration buildServerIdArray
INFO: JVM unique ID: <spring-btm>
Aug 14, 2008 12:35:05 PM bitronix.tm.recovery.Recoverer run
INFO: recovery committed 0 dangling transaction(s) and rolled back 0 aborted transaction(s) on 0 resource(s) []
752  [main] INFO  org.springframework.transaction.jta.JtaTransactionManager  - Using JTA UserTransaction: a BitronixTransactionManager with 0 in-flight transaction(s)
768  [main] INFO  org.springframework.transaction.jta.JtaTransactionManager  - Using JTA TransactionManager: a BitronixTransactionManager with 0 in-flight transaction(s)
768  [main] INFO  com.example.startup.StartupSvc  - Shutting down
768  [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext  - Closing org.springframework.context.support.ClassPathXmlApplicationContext@480457: display name [org.springframework.context.support.ClassPathXmlApplicationContext@480457]; startup date [Thu Aug 14 12:35:05 EDT 2008]; root of context hierarchy
799  [main] INFO  org.springframework.beans.factory.support.DefaultListableBeanFactory  - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@d19bc8: defining beans [btmConfig,BitronixTransactionManager,JtaTransactionManager]; root of factory hierarchy
Aug 14, 2008 12:35:05 PM bitronix.tm.BitronixTransactionManager shutdown
INFO: shutting down Bitronix Transaction Manager

I had commented out the main beans file and the hibernate beans files while I got the BTM beans going… now let’s uncomment those and see what we get…

Still a NoInitialContextException

Hmm, we’re still getting a NoInitialContextException when we try to create the queueConnectionFactory bean based on the JNDI lookup:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘queueConnectionFactory’: Invocation of init method failed; nested exception is javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file:  java.naming.factory.initial

[More time passes…]

Googling this error turned up a chapter called Using JNDI Outside J2EE from the book Teach Yourself J2EE in 21 Days, 2nd Edition, published by Sams.  Says here you need a jndi.properties file in your classpath, and in that file you need a java.naming.factory.initial property set to the name of your initial context factory class.  Hmm, I haven’t been able to find a jndi package in the BTM Javadoc… how ’bout if I check the BTM 1.3-RC2 source code I checked out?  Sure enough, there is a bitronix.tm.jndi package with a BitronixInitialContextFactory class in there.  I was looking at the wrong Javadoc! I was using the “current” BTM javadoc, which doesn’t have the jndi package.  I needed the 1.3 Javadoc.  The explanation at the top of the BitronixInitialContextFactory class assures me that this is all we need in jndi.properties:

<pre>java.naming.factory.initial = bitronix.tm.jndi.BitronixInitialContextFactory</pre>

(Good, because I wasn’t sure what to put for the java.naming.provider.url property mentioned in the book chapter!)

Name Not Found

Ok, now we’ve gotten past the NoInitialContextException to a NameNotFoundException:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘queueConnectionFactory’: Invocation of init method failed; nested exception is javax.naming.NameNotFoundException: JNDI object with [java:activemq/QueueConnectionFactory] not found: JNDI implementation returned null

I posted my question to the BTM user forum.  We’ll see how it turns out!

Advertisements

, , ,

  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s