Atomikos、Tomcat、JTA java.lang.ClassCastException

Posted

技术标签:

【中文标题】Atomikos、Tomcat、JTA java.lang.ClassCastException【英文标题】:Atomikos, Tomcat, JTA java.lang.ClassCastException 【发布时间】:2013-06-19 04:46:27 【问题描述】:

尝试使用 Tomcat、Atomikos、ActiveMQ 和 mysql 使事务正常工作。遵循这些示例: Tomcat 7 Integration with Atomikos 3.5.2 和Tomcat 7.0.27 Integration with Atomikos 3.7.1 没有成功...

ERROR: com.atomikos.icatch.jta.UserTransactionManager cannot be cast to javax.transaction.TransactionManager
java.lang.ClassCastException: com.atomikos.icatch.jta.UserTransactionManager cannot be cast to javax.transaction.TransactionManager

使用:

Tomcat 7.0.29 Atomikos 3.7.1 ActiveMQ 5.8.0 MySQL 5.5.31

将以下 jar 放到 $TOMCAT_HOME/lib 下

atomikos-integration-extension-3.7.1-20120529.jar atomikos-util-3.7.1.jar transactions-3.7.1.jar transactions-api-3.7.1.jar transactions-jdbc-3.7.1.jar transactions-jms-3.7.1.jar transactions-jta-3.7.1.jar geronimo-jta_1.0.1B_spec-1.0.jar activemq-all-5.8.0.jar mysql-connector-java-5.1.25.jar

这里是 transactions.properties 的内容

com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
com.atomikos.icatch.automatic_resource_registration=true
com.atomikos.icatch.output_dir=../work/atomikos
com.atomikos.icatch.log_base_dir=../work/atomikos/log
com.atomikos.icatch.enable_logging=true
com.atomikos.icatch.console_log_level=TRACE

context.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <Context>
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <Transaction factory="com.atomikos.icatch.jta.UserTransactionFactory" />

    <Resource name="TransactionManager"
          auth="Container"
          type="com.atomikos.icatch.jta.UserTransactionManager"
          factory="org.apache.naming.factory.BeanFactory" />

    <Resource name="UserTransaction"
          auth="Container"
          type="com.atomikos.icatch.jta.UserTransactionImp"
          factory="org.apache.naming.factory.BeanFactory" />                 

    <Resource name="jms/ConnectionFactory"
            auth="Container"
            description="JMS Connection Factory"
            type="com.atomikos.jms.AtomikosConnectionFactoryBean"
            factory="com.atomikos.tomcat.EnhancedTomcatAtomikosBeanFactory"
            uniqueResourceName="jms/ConnectionFactory"
            xaConnectionFactoryClassName="org.apache.activemq.ActiveMQXAConnectionFactory"
            xaProperties.brokerURL="vm://localhost"
            xaProperties.transportType="1"
            localTransactionMode="true" />

    <Resource name="jms/WsTopic" 
          auth="Container"
          type="org.apache.activemq.command.ActiveMQTopic" 
          factory="org.apache.activemq.jndi.JNDIReferenceFactory"
          physicalName="WS.TOPIC" />

    <Resource name="jms/WsQueue" 
          auth="Container"
          type="org.apache.activemq.command.ActiveMQQueue"
          factory="org.apache.activemq.jndi.JNDIReferenceFactory" 
          physicalName="WS.QUEUE" />


   <!--  MySQL -->
   <Resource name="jdbc/DB"
          auth="Container"
          type="com.atomikos.jdbc.AtomikosDataSourceBean"
          factory="com.atomikos.tomcat.EnhancedTomcatAtomikosBeanFactory"
          uniqueResourceName="jdbc/DB"
          minPoolSize="5"
          maxPoolSize="10"
          testQuery="SELECT 1 FROM DUAL"             
          xaDataSourceClassName="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"
          xaProperties.databaseName="db"
          xaProperties.serverName="localhost"
          xaProperties.port="3306"
          xaProperties.user="user"
          xaProperties.password="password"
          xaProperties.url="jdbc:mysql://localhost:3306/db"
          xaProperties.pinGlobalTxToPhysicalConnection="true"
          xaProperties.autoReconnect="true"
          xaProperties.autoReconnectForConnectionPools="true"
          xaProperties.autoReconnectForPools="true" />
</Context>

相关java代码:

import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
...

Hashtable<String, String> hashTable = new Hashtable<String, String>();
try 
    Context ctx = new InitialContext(hashTable);
    ctx = (Context) jndiContext.lookup("java:comp/env");

    TransactionManager transactionManager = (TransactionManager) ctx.lookup("TransactionManager");
 catch (Exception e) 
    e.printStackTrace();

我做错了什么?

编辑:

从 $TOMCAT_HOME/lib 中删除 activemq-all-5.8.0.jar 后,我能够克服原来的错误,但现在我得到了:

com.atomikos.jms.AtomikosJMSException: Error in proxy
at com.atomikos.jms.AtomikosJMSException.throwAtomikosJMSException(AtomikosJMSException.java:54)
at com.atomikos.jms.ConsumerProducerSupport.handleException(ConsumerProducerSupport.java:61)
at com.atomikos.jms.AtomikosJmsMessageConsumerProxy.receive(AtomikosJmsMessageConsumerProxy.java:73)
at com.atomikos.jms.AtomikosJmsMessageConsumerProxy.receive(AtomikosJmsMessageConsumerProxy.java:137)
...
at java.lang.Thread.run(Thread.java:619)
Caused by: com.atomikos.jms.AtomikosTransactionRequiredJMSException: The JMS session you are using requires a JTA transaction context for the calling thread and none was found.
Please correct your code to do one of the following: 
1. start a JTA transaction if you want your JMS operations to be subject to JTA commit/rollback, or
2. increase the maxPoolSize of the AtomikosConnectionFactoryBean to avoid transaction timeout while waiting for a connection, or
3. create a non-transacted session and do session acknowledgment yourself, or
4. set localTransactionMode to true so connection-level commit/rollback are enabled.
    at com.atomikos.jms.AtomikosTransactionRequiredJMSException.throwAtomikosTransactionRequiredJMSException(AtomikosTransactionRequiredJMSException.java:38)
    at com.atomikos.jms.ConsumerProducerSupport.enlist(ConsumerProducerSupport.java:107)
    at com.atomikos.jms.AtomikosJmsMessageConsumerProxy.receive(AtomikosJmsMessageConsumerProxy.java:70)
    ... 5 more

【问题讨论】:

【参考方案1】:

Tomcat 使用multiple class-loaders。请参阅their definitions,它是如何工作的以及哪个优先(引导、系统、webapp、common)。我假设您将geronimo-jta_1.0.1B_spec-1.0.jar 和/或transactions-jta-3.7.1.jar 放在WAR 文件webapp.war/WEB-INF/lib/$TOMCAT_HOME/lib/ 中。这可能会导致问题,因为 javax.transaction.TransactionManager 类将由不同的类加载器多次加载。即使该类具有相同的名称,如果它由不同的类加载器加载,转换也会失败。

    当 Tomcat 初始化定义在context.xmlcom.atomikos.icatch.jta.UserTransactionManager 实现。 (Tomcat 目前不使用任何 webapp 类加载器。)

    您在 Web 应用程序中使用了 javax.transaction.TransactionManager 类,现在它将使用 webapp 类加载器从位于 webapp.war/WEB-INF/lib/ 文件夹中的 JAR 文件的副本中加载在 WAR 文件中。

尝试从webapp.war/WEB-INF/lib/ 中删除包含javax.transaction.TransactionManager 类的JAR 文件并尝试重新部署您的应用程序。

或者,在CLASSPATH 系统变量中列出这些 JAR 文件,这样这些类将由在 Tomcat 中具有优先权的系统类加载器加载。

【讨论】:

感谢您的详细回复。 activemq-all-5.8.0.jar 从 webapp.war/WEB-INF/lib/ 移动到 $TOMCAT_HOME/lib/。但是现在我面临一个不同的错误:ERROR: Error in proxy com.atomikos.jms.AtomikosJMSException 能否请您添加一个源代码,您是如何使用 JMS 的?可能你忘记调用 UserTransaction.begin() 来启动事务了。 根据 [javax.jms.Session docs] (docs.oracle.com/javaee/6/api/javax/jms/Session.html) "...由于 Java 分布式事务是通过 Java Transaction API (JTA) 控制的,因此使用会话的提交并且禁止在这种情况下使用回滚方法。” 但是,此代码在不同的应用服务器/jms 提供程序下工作正常。 Ad localTransactionMode: 您只需通过配置 localTransactionMode="true" 关闭 ActiveMQ 的两阶段提交协议。 Atomikos 不会将 XA 事务与您的 ActiveMQ 资源一起使用,它会在提交此资源时仅使用单阶段本地事务协议。如果您只有一个这样的资源并且如果 (!) Atomikos 正确实现了允许一个非 XA 资源参与分布式事务的 last resource gambit 优化,那可能仍然可以。 Ad session.commit(): 实际上,如果资源参与 JTA (XA),则不能使用 JMS session.commit() 或 JDBC connection.commit()由事务管理器控制的事务。在开始使用资源之前,使用 UserTransaction.begin() 启动 JTA 事务。包装连接工厂(例如 AtomikosConnectionFactoryBean)的 Atomikos 代理类创建 Atomikos 包装的会话/连接,以自动将它们登记到当前 JTA 事务(绑定到线程上下文),当您调用 UserTransaction.commit() 时,Atomikos 将提交它们全部。

以上是关于Atomikos、Tomcat、JTA java.lang.ClassCastException的主要内容,如果未能解决你的问题,请参考以下文章

使用Atomikos实现JTA分布式事务

atomikos的Jta配置

将 JTA 属性设置为 Atomikos + Spring Boot 配置

spring+mybatis+Atomikos JTA事务配置说明

spring3.0+Atomikos 构建jta的分布式事务

spring3.0+Atomikos 构建jta的分布式事务