I’ve spent the past two days trying to get a distributed secondary Hibernate cache working with a Spring 3 application. The application is web-based running on JBoss 5.1 so I figured the best approach would be to use JBoss Cache, since it’s automatically configured and available in JNDI when you use the “all” configuration.
Hibernate 3.3.2 is configured inside of Spring using the Annotation-based session factory bean. Because I’m using JTA to manage transactions and Hibernate’s current session, I need to make sure that the secondary cache, whatever I choose, is aware of the transaction manager. I originally had EHCache 2.0.1 hooked into Hibernate via Hibernate configuration parameters passed into Spring’s bean. I was not setting the cache factory parameter on this bean. Everything works fine in this configuration and it recognizes the JTA transactions.
This application needs to be clustered horizontally – ensuring each component of the solution is failover-ready. JBoss Cache 2 is baked into JBoss AS 5.1 and a logical choice to pick. Hibernate has an extension JAR and it’s a simple interface, especially when you’re pulling out the cache from JNDI.
The problem? The JTA transaction manager that Spring proxies isn’t available to JBoss Cache. JBoss Cache (on JBoss AS) runs at the container-level inside of a different classloader. Spring holds onto the reference to the transaction manager proxy inside of a ThreadLocal variable which is NOT accessible to the container. To get around this, I tried using Hibernate’s implementation of the JBoss Transaction Manager lookup class, thinking since it’s JTA it’ll work.
It didn’t.
When I tried bringing up the application, I ran into errors surrounding the current transaction. At one point, the transaction was timing out during a cache pre-load process the application has. In another configuration, the application loaded but after a few minutes the app exceptioned out with a stale JDBC connection to the database. It was obvious to me that the JTA transaction surrounding the cache didn’t sync with the Spring JTA transaction.
I eventually gave up on JBoss Cache and am going with EHCache 2.0 using JGroups to synchronize each node. The “all” configuration of JBoss AS 5.1 has both JGroups and JBoss Cache preconfigured. I hooked into the synchronous JGroups UDP configuration, using the same JVM parameters the JBoss XML files use so that my app doesn’t need a special deployment for each server. Theoretically, the configuration will work in a cluster without having to change anything around.
We’ll see how the clustering works later on but for now, single node in cluster mode is working just fine.