Java – Atomikos + Jetty in a Maven build environment – Classloader issue

atomikosjavajettyjtamaven-2

I am working on a project with multiple JDBC data sources and JTA. I use Maven as a build tool, and I'd like to use the Jetty plugin (6.1.20) to run the application during development.

I am trying to configure Jetty to use Atomikos as the transaction manager. I'm following the Atomikos documentation from Jetty, but the Jetty startup fails with what looks like a class loader issue.

Here are the relevant bits of my configuration.

runtime scope dependencies in my pom.xml:

<dependency>
    <groupId>com.atomikos</groupId>
    <artifactId>transactions-jta</artifactId>
    <version>3.5.7</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>com.atomikos</groupId>
    <artifactId>transactions-jdbc</artifactId>
    <version>3.5.7</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>javax.transaction</groupId>
    <artifactId>jta</artifactId>
    <version>1.1</version>
    <scope>runtime</scope>
</dependency>

My jetty-env.xml located in /WEB-INF (autodiscovered by Jetty):

<New id="userTxImpl" class="com.atomikos.icatch.jta.UserTransactionImp" />

<New id="tx" class="org.mortbay.jetty.plus.naming.Transaction">
    <Arg>
        <Ref id="userTxImpl" />
    </Arg>
</New>

The instantiation of tx at the end is the bit that fails, if I leave it out, Jetty starts up fine (but does not expose UserTransaction via JNDI, of course).

The Stacktrace:

    2009-09-02 18:42:18.910::WARN:  Config error at <New id="tx" class="org.mortbay.jetty.plus.naming.Transaction"><Arg>
            <Ref id="userTxImpl"/>
        </Arg></New>
2009-09-02 18:42:18.910::WARN:  Failed startup of context org.mortbay.jetty.plugin.Jetty6PluginWebAppContext@12515db{/administrator,C:\workspace\administrator\src\main\webapp}
java.lang.IllegalStateException: No Constructor: <New id="tx" class="org.mortbay.jetty.plus.naming.Transaction"><Arg>
            <Ref id="userTxImpl"/>
        </Arg></New> on org.mortbay.jetty.plugin.Jetty6PluginWebAppContext@12515db{/administrator,C:\workspace\administrator\src\main\webapp}
    at org.mortbay.xml.XmlConfiguration.newObj(XmlConfiguration.java:631)
    at org.mortbay.xml.XmlConfiguration.configure(XmlConfiguration.java:256)
    at org.mortbay.xml.XmlConfiguration.configure(XmlConfiguration.java:190)
    at org.mortbay.jetty.plus.webapp.EnvConfiguration.configureWebApp(EnvConfiguration.java:130)
    at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1247)
    at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:517)
    at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:467)
    at org.mortbay.jetty.plugin.Jetty6PluginWebAppContext.doStart(Jetty6PluginWebAppContext.java:124)
    at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
    at org.mortbay.jetty.handler.HandlerCollection.doStart(HandlerCollection.java:152)
    at org.mortbay.jetty.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:156)
    at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
    at org.mortbay.jetty.handler.HandlerCollection.doStart(HandlerCollection.java:152)
    at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
    at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
    at org.mortbay.jetty.Server.doStart(Server.java:224)
    at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
    at org.mortbay.jetty.plugin.Jetty6PluginServer.start(Jetty6PluginServer.java:132)
    at org.mortbay.jetty.plugin.AbstractJettyMojo.startJetty(AbstractJettyMojo.java:441)
    at org.mortbay.jetty.plugin.AbstractJettyMojo.execute(AbstractJettyMojo.java:383)
    at org.mortbay.jetty.plugin.AbstractJettyRunMojo.execute(AbstractJettyRunMojo.java:210)
    at org.mortbay.jetty.plugin.Jetty6RunMojo.execute(Jetty6RunMojo.java:184)
    at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:579)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:498)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegmentForProject(DefaultLifecycleExecutor.java:265)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:191)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:149)
    at org.apache.maven.DefaultMaven.execute_aroundBody0(DefaultMaven.java:223)
    at org.apache.maven.DefaultMaven.execute_aroundBody1$advice(DefaultMaven.java:304)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:1)
    at org.apache.maven.embedder.MavenEmbedder.execute_aroundBody2(MavenEmbedder.java:904)
    at org.apache.maven.embedder.MavenEmbedder.execute_aroundBody3$advice(MavenEmbedder.java:304)
    at org.apache.maven.embedder.MavenEmbedder.execute(MavenEmbedder.java:1)
    at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:176)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:63)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:408)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:351)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:31)

This looks like a classloader issue, as org.mortbay.jetty.plus.naming.Transaction has got a Constructor taking a javax.transaction.UserTransaction, and com.atomikos.icatch.jta.UserTransactionImp implements javax.transaction.UserTransaction, yet Jetty complains it does not have a proper constructor to use.

I'm obviously missing something here, but what is it?

Thanks!

Best Solution

You may have multiple copies of the UserTransaction class kicking around in your deployment. If these are loaded from different classloaders, then you can end up with this sort of error.

Check Jetty's libraries for UserTransaction, and then check your application's libraries. You should only have one copy.