13 September 2006

Unnecessary transitive Maven2 dependencies considered harmful

While deploying an EAR, I kept getting this error when instantiating my Spring beans which happen to use Hibernate to talk to the database:


java.lang.ClassCastException: org.jboss.tm.TxManager
at org.hibernate.transaction.JNDITransactionManagerLookup.getTransactionManager(JNDITransactionManagerLookup.java:23)
at org.hibernate.impl.SessionFactoryImpl.(SessionFactoryImpl.java:302)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1176)
at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:825)


Of course, good old ClassCastException doesn't tell me what class it expected, only what class it got. From looking at the javadocs, its expecting a javax.transaction.TransactionManager. However, org.jboss.tm.TxManager does, in fact, implement that interface.

So what gives? Well, its going to be a classloader issue. Again, ClassCastException (or most likely the caller) fails to realize that a class's name isn't enough to uniquely identify a class. The classloader that loads it is very important.

Given that I'm using Maven2, I figured that I must be including a copy of javax.transaction.TxManager somewhere.

This command will list the contents of all jars:
find -name '*.jar' -exec unzip -l '{}' \;

Redirect that to a text file, then grep for javax/transaction/TransactionManager

I found I was bundling jta-1.0.1B.jar in my ear. How did that get there? Transitive dependencies, of course. I didn't have to guess too hard as to who might be including that: Hibernate. Now, I thought I was already exlcuding the jta dependency. Turns out I'd only done it in a sub-project. I decided to fix it for all hibernate dependencies in my project by adding it in the parent pom.xml:


<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.1.3</version>
<exclusions>
<!--
This is container-provided; we don't need the dependency here;
In fact, java.lang.ClassCastException: org.jboss.tm.TxManager may occur if we
accidentally include a jta jar
-->
<exclusion>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
</exclusion>
</exclusions>
</dependency>


With that, the problem is solved.

3 comments:

Holgi said...

Wonderful! I had the same problem and couldn't find a solution... *doh*! :)

I also found out, that you can omit the jta-dependency by declaring it "provided", like:

<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.0.1B</version>
<scope>provided</scope>
</dependency>

This might make it easier to "throw out" another unwanted dependency w/o digging thru the pom's.

HTH.

Tim said...

Good point. I've used that technique also, especially when trying to exclude a dependency that has been included by numerous other poms.

Of course, marking it "provided" still means it is on the compile and test classpath, but that usually isn't an issue.

sonicfab said...

Thanks a lot !
I was stuck on that problem for several hours, your info really made my day.