Transactional Client
Camel recommends supporting the Transactional Client from the EIP patterns using spring transactions.
Transaction Oriented Endpoints (Camel Toes) like JMS support using a transaction for both inbound and outbound message exchanges. Endpoints that support transactions will participate in the current transaction context that they are called from.
You should use the SpringRouteBuilder to setup the routes since you will need to setup the spring context with the TransactionTemplates that will define the transaction manager configuration and policies.
For inbound endpoint to be transacted, they normally need to be configured to use a Spring PlatformTransactionManager. In the case of the JMS component, this can be done by looking it up in the spring context.
You first define needed object in the spring configuration.
<bean id="jmsTransactionManager" 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>
Then you look them up and use them to create the JmsComponent.
PlatformTransactionManager transactionManager = (PlatformTransactionManager) spring.getBean("jmsTransactionManager"); ConnectionFactory connectionFactory = (ConnectionFactory) spring.getBean("jmsConnectionFactory"); JmsComponent component = JmsComponent.jmsComponentTransacted(connectionFactory, transactionManager); component.getConfiguration().setConcurrentConsumers(1); ctx.addComponent("activemq", component);
Transaction Policies
Outbound endpoints will automatically enlist in the current transaction context. But what if you do not want your outbound endpoint to enlist in the same transaction as your inbound endpoint? The solution is to add a Transaction Policy to the processing route. You first have to define transaction policies that you will be using. The policies use a spring TransactionTemplate to declare the transaction demarcation use. So you will need to add something like the following to your spring xml:
<bean id="PROPAGATION_REQUIRED" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="jmsTransactionManager"/> </bean> <bean id="PROPAGATION_NOT_SUPPORTED" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="jmsTransactionManager"/> <property name="propagationBehaviorName" value="PROPAGATION_NOT_SUPPORTED"/> </bean> <bean id="PROPAGATION_REQUIRES_NEW" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="jmsTransactionManager"/> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRES_NEW"/> </bean>
Then in your SpringRouteBuilder, you just need to create new SpringTransactionPolicy objects for each of the templates.
public void configure() { ... Policy requried = new SpringTransactionPolicy(bean(TransactionTemplate.class, "PROPAGATION_REQUIRED")); Policy notsupported = new SpringTransactionPolicy(bean(TransactionTemplate.class, "PROPAGATION_NOT_SUPPORTED")); Policy requirenew = new SpringTransactionPolicy(bean(TransactionTemplate.class, "PROPAGATION_REQUIRES_NEW")); ... }
Once created, you can use the Policy objects in your processing routes:
// Send to bar in a new transaction from("activemq:queue:foo").policy(requirenew).to("activemq:queue:bar"); // Send to bar without a transaction. from("activemq:queue:foo").policy(notsupported ).to("activemq:queue:bar");
Transaction Policies improvements in Camel 1.4
In Camel 1.4 we have eased the syntax to setup the transaction polices directly on the SpringTransactionPolicy
object:
<bean id="PROPAGATION_REQUIRED" class="org.apache.camel.spring.spi.SpringTransactionPolicy"> <property name="transactionManager" ref="jmsTransactionManager"/> </bean> <bean id="PROPAGATION_REQUIRES_NEW" class="org.apache.camel.spring.spi.SpringTransactionPolicy"> <property name="transactionManager" ref="jmsTransactionManager"/> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRES_NEW"/> </bean>
And the Java DSL is a bit simpler now:
Policy requried = bean(SpringTransactionPolicy.class, "PROPAGATION_REQUIRED"));
Database Sample
In this sample we want to ensure that two endpoints is under transaction control. These two endpoints inserts data into a database.
The sample is in its full as a unit test.
First of all we setup the usual spring stuff in its configuration file. Here we have defined a DataSource to the HSQLDB and a most importantly
the Spring DataSoruce TransactionManager that is doing the heavy lifting of ensuring our transactional policies. You are of course free to use any
of the Spring based TransactionMananger, eg. if you are in a full blown J2EE container you could use JTA or the WebLogic or WebSphere specific managers.
We use the required transaction policy that we define as the PROPOGATION_REQUIRED
spring bean. And as last we have our book service bean that does the business logic
and inserts data in the database as our core business logic.
In our Camel route that is Java DSL based we setup the transactional policy, wrapped as a Policy.
Then we are ready to define our Camel routes. We have two routes: 1 for success conditions, and 1 for a forced rollback condition.
This is after all based on a unit test.
As its a unit test we need to setup the database and this is easily done with Spring JdbcTemplate
And our core business service, the book service, will accept any books except the Donkeys.
Then we are ready to fire the tests. First to commit condition:
And lastly the rollback condition since the 2nd book is a Donkey book:
JMS Sample
In this sample we want to listen for messages on a queue and process the messages with our business logic java code and send them along. Since its based on a unit test the destination is a mock endpoint.
This time we want to setup the camel context and routes using the Spring XML syntax.
Since the rest is standard XML stuff its nothing fancy now for the reader:
Our business logic is set to handle the incomming messages and fail the first two times. When its a success it responds with a Bye World
message.
And our unit test is tested with this java code. Notice that we expect the Bye World
message to be delivered at the 3rd attempt.
Spring based configuration
In Camel 1.4 we have introduced the concept of configuration of the error handlers using spring XML configuration. The sample below demonstrates that you can configure transaction error handlers in Spring XML as spring beans. These can then be set as global, per route based or per policy based error handler. The latter has been demonstrated in the samples above. This sample is the database sample configured in Spring XML.
Notice that we have defined two error handler, one per route. The first route uses the transaction error handler, and the 2nd uses no error handler at all.
The following snippet is the Spring XML configuration to setup the error handlers in pure spring XML:
DelayPolicy
DelayPolicy is a new policy introduced in Camel 1.5, to replaces the RedeliveryPolicy used in Camel 1.4.
Notice the transactionErrorHandler
can be configured with a DelayPolicy to set a fixed delay in millis between each redelivery attempt.
See Also
Using This Pattern
If you would like to use this EIP Pattern then please read the Getting Started, you may also find the Architecture useful particularly the description of Endpoint and URIs. Then you could try out some of the Examples first before trying this pattern out.