There are sometimes a bunch of things that you have to get right, and getting any one of them wrong keeps you from being where you need to be. (I guess it’s a lot worse for space shuttle engineers!)
Things I Had Wrong
Here are six issues, mostly from today, that all needed to be corrected for my project to work:
- I didn’t have a jndi.properties file with a java.naming.factory.initial setting pointing to the bitronix.tm.jndi.BitronixInitialContextFactory. This was causing a NoInitialContextException.
- I didn’t realize that Bitronix Transaction Manager supports (at least) two types of config file: the transaction manager config file and the resource loader config file. I was putting settings in the transaction manager config file (defaults to bitronix-default-config.properties) that belong in the release loader config file. This caused those settings to be ignored by BTM (if my bitronix-default-config.properties file is being read at all, which I’m not sure of).
- The bitronix.tm.resource.configuration property by which you can specify a resource loader config file expects a path relative to the folder in which you started the virtual machine, not relative to the classpath, so my resource loader configuration was never being found (that’s also why I got a NullPointerException when I tried to set the resourceConfigurationFilename property on the btmConfig bean).
- The uniqueName I was using for the JMS ConnectionFactory was java:activemq/QueueConnectionFactory, but BTM’s JNDI implementation doesn’t support the colon-ized namespace (i.e., I needed to remove the “java:” from the JNDI-name). [Update 8/26/2008: It turns out you can use the java:-prefixed with BTM if you do it right… ]
- I neglected to set the init-method and destroy-method on my PoolingConnectionFactory bean, so the my org.apache.activemq.ActiveMQXAConnectionFactory underlying my bitronix.tm.resource.jms.PoolingConnectionFactory never got initialized (causing the uniqueName to never be registered with the JNDI server).
- I misspelled the name of the underlying class for my connection factory bean — as org.activemq.ActiveMQXAConnectionFactory, when it’s actually org.apache.activemq.ActiveMQXAConnectionFactory — so I got a “ResourceConfigurationException: cannot create JMS connection factory named QueueConnectionFactory” error that had as its cause farther down a “java.lang.ClassNotFoundException: org.activemq.ActiveMQXAConnectionFactory”
I had also specified the wrong implementation of the slf4j-api (slf4j-jdk14 instead of slf4j-log4j12) in my pom file, which meant I didn’t see all of BTM’s messages when I set the logging level to DEBUG in log4j.properties. This didn’t directly keep my project from doing what it oughta, but it might have slowed the problem analysis.
These are things that at one time or another I suspected I needed to change, but that ended up not being part of the problem:
- Adding a jndiEnvironment section to the JndiObjectFactoryBean definition for the queueConnectionFactory. This would have resulted in a queueConnectionFactory bean definition like this:
<bean id="queueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:activemq/QueueConnectionFactory" /> <property name="jndiEnvironment"> <props> <prop key="java.naming.factory.initial">bitronix.tm.jndi.BitronixInitialContextFactory</prop> </props> </property> </bean>
This was taken care of by having this setting in the jndi.properties file.
- Adding a
depends-onattribute to the BitronixTransactionManager bean and/or queueConnectionFactory JndiObjectFactoryBean explicitly telling Spring which other beans needed to be instantiated before these beans. Seems to work fine without that.
bitronix.tm.resource.bind = trueto a
bitronix-default-config.propertiesin the root of the classpath. The JNDI lookup seems to be doing fine without explicitly setting this property.
Two quick points:
- Experience will be a great help here, as inexperience with JNDI and BTM were a big part of why I took all these wrong turns; and
- Hallelujah that it’s Friday! :)
Where We Are Now
I have JNDI lookup working on both a JTA transaction manager and a JMS connection factory, kicked off from a JUnit-based integration test.
I need to get my JDBC datasource hooked up similarly, on the way to getting my whole XA example working as a JUnit test — sending a message to the JMS queue, receiving it and writing to the database, demonstrating both the commit and a rollback situations.