Posts Tagged Bitronix

Blowing up in test mode

This is the final post in a 9-part series on Envers, Spring, and JTA. The previous post is The final answer.


Setting the hibernate.transaction.manager_lookup_class and org.hibernate.transaction.JTATransactionFactory properties in the Hibernate config:

<prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</prop>
<prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</prop>

fixed my issue where the envers beforeCompletion synchronization wasn’t-getting-called-when-we’re-in-JTA-mode… but with this change, the integration test stopped working — it now couldn’t find the JTA transaction manager by JNDI lookup.

Ah, yes — we’d need a JTA transaction manager to actually be available at integration test time — looks like another job for Bitronix Transaction Manager.

1. How I Set Up BTM

So, I put a jndi.properties file in the root of the classpath under src/test/java/ …

java.naming.factory.initial = bitronix.tm.jndi.BitronixInitialContextFactory

…put a bitronix-default-config.properties file beside it…

bitronix.tm.serverId = hibernate-support-module-btm-server
bitronix.tm.journal.disk.logPart1Filename = target/btm1.log
bitronix.tm.journal.disk.logPart2Filename = target/btm2.log

…and added a bitronixTransactionManager bean to the beans file under src/test/resources…

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

2. One More Piece

The transaction manager JNDI lookup still was failing though, with this error:

org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is org.hibernate.TransactionException: Naming service lookup for UserTransaction returned null [UserTransaction]

Caused by: org.hibernate.TransactionException: Naming service lookup for UserTransaction returned null [UserTransaction]

The BTM Hibernate documentation pointed out that I can set the

hibernate.transaction.manager_lookup_class to Hibernate’s BTMTransactionManagerLookup class.  (Huh!)

<prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.BTMTransactionManagerLookup</prop>

When I do this, the integration test now works again.

3. Now the Deploy to JBoss Fails

I was pretty sure that deploying to JBoss with the transaction manager lookup setting pointing to a BTMTransactionManagerLookup wasn’t going to work, and sure enough, on deploy I get this:

org.hibernate.HibernateException: Could not obtain BTM transaction manager instance

Caused by: java.lang.ClassNotFoundException: bitronix.tm.TransactionManagerServices

Right, this error is because we aren’t including BTM in the production war project (and we don’t want to).

4. Test and Production Both Need To Work At Once

So now we can run our tests if we specify the BTMTransactionManagerLookup, and we can run in production if we specify the JBossTransactionManagerLookup.  We need to find a configuration that will work both in test and production modes.

Another nagging issue is that we don’t want to hardcode our production configuration to be specific to JBoss — ideally we’d like the application server to be auto-detected; if we need to we could make it manually configurable.  Hardcoded JBossTransactionManagerLookup won’t do.

5. Spring to the Rescue?

Thinking of auto-detecting the application server reminds me of what Spring’s JtaTransactionManager does — it tries various JNDI lookups for the JTA TransactionManager and UserTransaction until it finds ones that work, so you’re not hardcoding support for only one app server and there is no user configuration needed .  I wonder if Spring has a TransactionManagerLookup that does that sort of thing to avoid  — is Spring’s LocalTransactionManagerLookup what we need?

When I switch the transaction manager lookup line to that:

<prop key="hibernate.transaction.manager_lookup_class">org.springframework.orm.hibernate3.LocalTransactionManagerLookup</prop>

…I get this error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘sessionFactory’ defined in class path resource [com/ontsys/fw/hibernatesupport/startup/hibernate-support-beans.spring.xml]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: Could not instantiate TransactionManagerLookup ‘org.springframework.orm.hibernate3.LocalTransactionManagerLookup’

Come to think of it, the javadoc for LocalTransactionManagerLookup does say:

Implementation of Hibernate’s TransactionManagerLookup interface that returns a Spring-managed JTA TransactionManager, determined by LocalSessionFactoryBean’s “jtaTransactionManager” property.

(Emphasis mine.)  My Hibernate configuration configures an AnnotationSessionFactoryBean (which is a LocalSessionFactoryBean), and I didn’t set the jtaTransactionManager property there.

5.1. Setting the jtaTransactionManager Property

As a first attempt, I set

<property name="jtaTransactionManager" ref="transactionManager" />

where the transactionManager reference refers to my

<tx:jta-transaction-manager />

bean.  I get:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘sessionFactory’ defined in class path resource [com/ontsys/fw/hibernatesupport/startup/hibernate-support-beans.spring.xml]: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type [org.springframework.transaction.jta.JtaTransactionManager] to required type [javax.transaction.TransactionManager] for property ‘jtaTransactionManager’; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [org.springframework.transaction.jta.JtaTransactionManager] to required type [javax.transaction.TransactionManager] for property ‘jtaTransactionManager’: no matching editors or conversion strategy found

So the Spring JtaTransactionManager isn’t a javax.transaction.TransactionManager.  What do you specify for the jtaTransactionManager property, then?

In a helpful post from 2004, Juergen Hoeller speaks to Spring’s support for, and the limits of, container-independent JTA configuration:

…Spring’s JtaTransactionManager itself works just nicely in a container-independent fashion – in typical usage scenarios where you could use a plain JTA javax.transaction.UserTransaction too…you just need container-specific configuration for more advanced usage, namely transaction suspension and Hibernate cache completion callbacks. The simple reason for this is that you need the JTA javax.transaction.TransactionManager to suspend/resume transactions and register transaction synchronizations; the UserTransaction interface does not support that functionality.

Unfortunately, J2EE does not specify how to access the JTA TransactionManager, just the common JNDI location of the JTA UserTransaction – so the lookup of the JTA TransactionManager has to be container-specific. This is the case with Hibernate (see TransactionManagerLookup), this is the case with Spring, this is the case with any such tool.

For Hibernate cache completion callbacks with JTA, you need to specify a corresponding TransactionManagerLookup in the Hibernate properties. As an alternative, Spring allows you to pass a JTA TransactionManager into LocalSessionFactoryBean’s “jtaTransactionManager” property, to reuse a centrally defined TransactionManager reference that can be shared with JtaTransactionManager’s “transactionManager” property.

(Emphasis mine.) Juergen goes on to mention that as another alternative, Hibernate’s cache-completion callbacks are taken care of if you use Spring’s own transaction synchronization mechanism — this avoids the need for a container-specific TransactionManagerLookup.  I don’t think Spring’s synchronization mechanism is sufficient for what we need here though, because it looks like it only supports the afterCompletion() callback, and we (through envers) are making use of the beforeCompletion() callback.

6. A Table of Our Apparent Options

Opt # Configuration Viable Option? Comments
1 Plain ol’ Spring JtaTransactionManager and Hibernate LocalSessionFactoryBean without additional configuration No Hibernate doesn’t know about Spring JTA transaction manager, synchronizations get registered wrong and are not called
2 Use Spring’s synchonization mechanism No Appears to only support afterCompletion() synchronization callback, and envers needs and uses the beforeCompletion() one
3 Have a separate javax.transaction.TransactionManager that the Hibernate LocalSessionFactoryBean and Spring’s JtaTransactionManager both reference Yes The javax.transaction.TransactionManager may have to be defined in a container-specific way
4 Specify hibernate.transaction.manager_lookup_class to Hibernate’s LocalSessionFactoryBean config Yes Which TransactionManagerLookup class is needed is container-specific, so we’d need to abstract the choice out to a configuration property somehow

I’m leaning toward going with option #4 without even trying option #3 since I can see how we would do #4 and I’m afraid that even with #3’s greater complexity it still wouldn’t get us container-independent JTA configuration.

Update (1/2/2009): Yep, option #4 is what we went with.   To abstract away the container-specificness, we maintain a list of org.hibernate.transaction.*TransactionManagerLookup classes and iterate through the list trying getTransactionManager() on each until we find one that doesn’t return null — then we populate the hibernate.transaction.factory_class configuration property with that class name.  The org.hibernate.transaction.BTMTransactionManagerLookup last in the list, and the org.hibernate.transaction.JBossTransactionManagerLookup is currently the only other lookup class in the list; but as we add support more app servers, the idea is we can just add more of the specific org.hibernate.transaction.*TransactionManagerLookup classes to the list.

, , ,

1 Comment

Why we’d want at least version 1.3.1

Bitronix Transaction Manager has been helpful for our integration tests, which can then run outside the app server and have a JTA transaction manager and enough of a JNDI provider to get by.

We have been using version 1.3, since that’s the version that first had the JNDI server.  But we had to specify disableJmx=true in the Configuration object so that the java: prefix could work.

This limitation has been removed as of BTM 1.3.1.  I’m BTM-enabling another module — maybe now’s the time to update to the current version of BTM…

, ,

Leave a comment

We’ll want to JNDI-lookup the queue, too…

In our system we have proved out looking up the JMS connection factory from JNDI, but up to this point we have declared the Queue bean using an <amq:queue> element in the application bean files.  This ties our application code to the ActiveMQ broker, which we don’t want.  We want to move toward looking up the connection factory and the queue from JNDI.  That way the application bean file only needs to know the JNDI name, and we can configure the queue in the connection factory datasource configuration file on the app server (e.g., activemq-jms-ds.xml), probably as an mbean element in there:


   <mbean code="org.jboss.resource.deployment.AdminObject" name="activemq.queue:name=TheMBeanNameYouSeeInTheJMXConsole">
      <attribute name="JNDIName">this/is/the/jndi/name/the/bean/files/will/use</attribute>
      <depends optional-attribute-name="RARName">jboss.jca:service=RARDeployment,name='activemq-rar-5.1.0.rar'</depends>
      <attribute name="Type">javax.jms.Queue</attribute>
      <attribute name="Properties">PhysicalName=theQueueName.YouWillSee.In.ActiveMQ</attribute>
   </mbean>

But Who Will Care for the Babies?

This will be fine for a production environment — but what about our integration tests?  Right now we use Bitronix Transaction Manager 1.3’s JNDI server facilities to provide JNDI lookup of the JDBC datasource for our integration tests.  I thought we’d also use BTM to look up the JMS connection factory… but BTM does not support JNDI-looking-up queues as well.  Ludovic says:

If you really need a complete JNDI server, you can use Tomcat’s which is quite easy to reuse. This is what I recommended using before BTM 1.3, there is an example here: http://docs.codehaus.org/display/BTM/Hibernate

That’s probably what we’ll need to look into when the time comes…

, , , , ,

Leave a comment

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.

, , , ,

3 Comments

HSQLDB Says ‘No’; Bitronix Chafes

The currently-released version of HSQLDB, 1.8.0, doesn’t support XA transactions (though I see work thereon in the trunk).  This doesn’t please Bitronix, which declares the situation an unsafe one in which to start up:

bitronix.tm.utils.InitializationException: recovery failed, cannot safely start the transaction manager
...
Caused by: bitronix.tm.resource.ResourceConfigurationException: property <className> for resource <ds1> in resources configuration file must be the name of a class implementing either javax.sql.XADataSource or javax.jms.XAConnectionFactory

(To be fair, it’s hardly Bitronix’s fault we supplied it with a class that didn’t implement a required interface!)

Hmm… will we:

Or maybe we should try the H2 database engine, which already supports XA transactions and boasts a javax.sql.XADataSource-implementing JdbcDataSource class to back that up?  I wonder how much of our testing infrastructure that would gum up, changing test-databases.

Update 8/21/2008 – There’s a way around this limitation — see comment #1!

, ,

2 Comments

On the classpath

I guess I’d rather be on the classpath than the warpath…

Anyway, Bitronix’s bitronix.tm.Configuration class currently only supports specifying the resource loader .properties file’s location relative to the JVM’s current directory (think “argument to the constructor of a File object”), but for unit testing we would like to be able to plop the .properties file in our classpath and not have to hardcode a path to it.  Basically we wish we could give BTM a classpath-relative properties path-n-filename.  For the moment, we’ll use this hack:

    @BeforeClass
    public static void beforeAllTests() {
        //...

        final String configurationFilePath = getAbsoluteFilePath("/bitronix-resource-loader.properties");

        TransactionManagerServices.getConfiguration()
            .setResourceConfigurationFilename(configurationFilePath);
        TransactionManagerServices.getTransactionManager();
    }

    private static String getAbsoluteFilePath(String classPathBasedFilePath) {
        URL path = ThisClass.class.getResource(classPathBasedFilePath);
        if (path == null) {
            throw new RuntimeException("Can't find resource " + classPathBasedFilePath);
        }
        return path.getFile();
    }

In our case, path.getFile() returns something along the lines of:

/C:/path/to/eclipse/workspace/and/project/target/test-classes/bitronix-resource-loader.properties

The leading slash is kind of funky, but it works.

,

2 Comments

Why we wanted a JNDI server for integration testing

Why is having a JNDI server for our integration testing environment so helpful?  Well, just for our minimal XA integration test example, there were (at one point) fourteen Spring beans:

If we had to use a non-JNDI strategy for integration testing outside the app server, seven of these beans would have to be swapped out for test-only doubles — but we want our configuration for integration testing to match as closely as possible our production configuration so that we’re testing the same thing we’re deploying!

, , , ,

Leave a comment