This article briefly introduces how Apache camel implements transaction control on route. It first introduces the situation that the entire route only involves one transaction participant, and then introduces the situation that the route involves multiple transaction participants. camel implements transaction control through integration with Spring framework.
1. The entire route has only one transaction participant, "Local transaction". Here we use the JMS example. The backend MQ is activemq. The example is shown as follows: (the image is from camel in action)
The route code is as follows:
public class JMSTransaction extends RouteBuilder { public void configure() throws Exception { TProcessor0 p0 = new TProcessor0(); TProcessor1 p1 = new TProcessor1(); from("jms:queue:TOOL.DEFAULT").process(p0).process(p1).to("file:d:/temp/outbox"); }}
The spring configuration is as follows:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:broker="http://activemq.apache.org/schema/core" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.2.0.xsd"> <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> <package> com.test.camel.transaction.jms </package> </camelContext> <bean id="jms" class="org.apache.activemq.camel.component.ActiveMQComponent"> <property name="transacted" value="true"/> <property name="transactionManager" ref="txManager"/> </bean> <bean id="txManager" class="org.springframework.jms.connection.JmsTransactionManager"> <property name="connectionFactory" ref="jmsConnectionFactory"/> </bean> <bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616"/> </bean></beans>
The logic defined by route is to retrieve messages from the queue and perform a series of processing (process (P0 ). process (P1), <property name = "transacted" value = "true"/> means that the Message Access through this JMS is subject to transaction control. the preceding route throws an exception in process (P1), and txmanager performs rollback processing. (In activemq, messages are redelivery to the client six times by default. If an exception persists, the messages are placed in the deadletter Queue (activemq. (dlq). activemq is required in the configuration file of acivemq. the configuration in XML is as follows: (the non-persistent queue message error is also transferred to dead
Letter queue)
<Policyentry queue = ">">
<Deadletterstrategy>
<Shareddeadletterstrategy processnonpersistent = "true"/>
</Deadletterstrategy>
If <property name = "transacted" value = "false"/> is returned, messages are lost six times after being resold.
If the transaction participant in the above example is a database, the principle is similar, but the configured Transaction Manager is different, for example:
<Bean id = "txmanager" class = "org. springframework. JDBC. datasource. cetcetransactionmanager"/>
Examples of using activemq JMS in camel can refer to the http://blog.csdn.net/kkdelta/article/details/7237096
2. Global transactions in camel. A route contains multiple transaction participants, as shown in the following figure: (picture from camel in action)
Route is defined as follows:
public class XaTransaction extends RouteBuilder { public void configure() throws Exception { TProcessor1 p1 = new TProcessor1(); from("jms:queue:TOOL.DEFAULT") .transacted() .log("+++ before database +++") .bean(SQLBean.class, "toSql") .to("jdbc:myDataSource") .process(p1) .log("+++ after database +++"); }}public class SQLBean { public String toSql(String str) { //create table CamelTEST(msg varchar2(2000)); StringBuilder sb = new StringBuilder(); sb.append("INSERT INTO CamelTEST VALUES ('camel test')"); return sb.toString(); }}
The logic of route is to retrieve messages from queue, operate the database, and perform other subsequent operations (process (P1). If process (P1) throws an exception, message retrieval and database operation rollback,
If the entire route is successfully completed, the request message and database operation are submitted.
Here we use JTA Transaction Manager is atomikos, the corresponding jar package can be downloaded from here: http://download.csdn.net/detail/kkdelta/4056226
Home http://www.atomikos.com/Main/ProductsOverview of atomikos
The spring configuration is as follows:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:broker="http://activemq.apache.org/schema/core" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.2.0.xsd"> <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> <package> com.test.camel.transaction.xa </package> </camelContext> <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close" > <!-- when close is called, should we force transactions to terminate or not? --> <property name="forceShutdown" value="false"/> </bean> <!-- this is some atomikos setup you must do --> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" > <property name="transactionTimeout" value="300"/> </bean> <!-- this is some atomikos setup you must do --> <bean id="connectionFactory" class="com.atomikos.jms.AtomikosConnectionFactoryBean" > <property name="uniqueResourceName" value="amq1"/> <property name="xaConnectionFactory" ref="jmsXaConnectionFactory"/> </bean> <!-- this is the Spring JtaTransactionManager which under the hood uses Atomikos --> <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" > <property name="transactionManager" ref="atomikosTransactionManager"/> <property name="userTransaction" ref="atomikosUserTransaction"/> </bean> <!-- Is the ConnectionFactory to connect to the JMS broker --> <!-- notice how we must use the XA connection factory --> <bean id="jmsXaConnectionFactory" class="org.apache.activemq.ActiveMQXAConnectionFactory" > <property name="brokerURL" value="tcp://localhost:61616"/> </bean> <!-- define the activemq Camel component so we can integrate with the AMQ broker below --> <bean id="jms" class="org.apache.activemq.camel.component.ActiveMQComponent" > <property name="transacted" value="true"/> <property name="transactionManager" ref="jtaTransactionManager"/> </bean> <bean id="myDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close"> <!-- set an arbitrary but unique name for the datasource --> <property name="uniqueResourceName"><value>XADBMS</value></property> <property name="xaDataSourceClassName"> <value>oracle.jdbc.xa.client.OracleXADataSource</value> </property> <property name="xaProperties"> <props> <prop key="user">xxx</prop> <prop key="password">xxx</prop> <prop key="URL">jdbc:oracle:thin:@147.151.240.xxx:1521:orcl</prop> </props> </property> <property name="poolSize" value="1"/> </bean> </beans>