Show Table of Contents
5.2. Demarcation by Transactional Endpoints
If a consumer endpoint at the start of a route accesses a resource, the
transacted()command is of no use, because it initiates the transaction after an exchange is polled. In other words, the transaction starts too late to include the consumer endpoint within the transaction scope. The correct approach in this case is to make the endpoint itself responsible for initiating the transaction. An endpoint that is capable of managing transactions is known as a transactional endpoint.
Sample route with JMS endpoint
Figure 5.2, “Demarcation by Transactional Endpoints” shows an example of a route that is made transactional by the presence of a transactional endpoint at the start of the route (in the
from()command). All of the route nodes are included in the transaction scope. In this example, all of the endpoints in the route access a JMS resource.
Figure 5.2. Demarcation by Transactional Endpoints
There are two different models of demarcation by transactional endpoint, as follows:
- General case—normally, a transactional endpoint demarcates transactions as follows: when an exchange arrives at the endpoint (or when the endpoint successfully polls for an exchange), the endpoint invokes its associated transaction manager to begin a transaction (attaching it to the current thread); and when the exchange reaches the end of the route, the transactional endpoint invokes the transaction manager to commit the current transaction.
- JMS endpoint with InOut exchange—when a JMS consumer endpoint receives an InOut exchange and this exchange is routed to another JMS endpoint, this must be treated as a special case. The problem is that the route can deadlock, if you try to enclose the entire request/reply exchange in a single transaction. For details of how to resolve this problem, see Section 3.3, “InOut Message Exchange Pattern”.
Route definition in Java DSL
The following Java DSL example shows how to define a transactional route by starting the route with a transactional endpoint:
from("jmstx:queue:giro") .to("jmstx:queue:credits") .to("jmstx:queue:debits");
Where the transaction scope encompasses the endpoints,
jmstx:queue:debits. If the transaction succeeds, the exchange is permanently removed from the
giroqueue and pushed on to the
creditsqueue and the
debitsqueue; if the transaction fails, the exchange does not get put on to the
debitsqueues and the exchange is pushed back on to the
giroqueue (by default, JMS will automatically attempt to redeliver the message).
The JMS component bean,
jmstx, must be explicitly configured to use transactions, as follows:
<beans ...> <bean id="jmstx" class="org.apache.camel.component.jms.JmsComponent"> <property name="configuration" ref="jmsConfig" /> </bean> <bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration"> <property name="connectionFactory" ref="jmsConnectionFactory"/> <property name="transactionManager" ref="jmsTransactionManager"/> <property name="transacted" value="true"/> </bean> ... </beans>
Where the transaction manager instance,
jmsTransactionManager, is associated with the JMS component and the
transactedproperty is set to
trueto enable transaction demarcation for InOnly exchanges. For the complete Spring XML configuration of this component, see Example 3.1, “JMS Transaction Manager Configuration”.
Route definition in Spring XML
The preceding route can equivalently be expressed in Spring XML, as follows:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ... > <camelContext xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="jmstx:queue:giro"/> <to uri="jmstx:queue:credits"/> <to uri="jmstx:queue:debits"/> </route> </camelContext> </beans>
transacted() not required
transacted()DSL command is not required in a route that starts with a transactional endpoint. Nevertheless, assuming that the default transaction policy is
PROPAGATION_REQUIRED(see Section 5.3, “Propagation Policies”), it is usually harmless to include the
transacted()command, as in this example:
from("jmstx:queue:giro") .transacted() .to("jmstx:queue:credits") .to("jmstx:queue:debits");
However, it is possible for this route to behave in unexpected ways—for example, if a single
TransactedPolicybean having a non-default propagation policy is created in Spring XML (see the section called “Default transaction manager and transacted policy”)—so it is generally better not to include the
transacted()DSL command in routes that start with a transactional endpoint.
The following Apache Camel components act as transactional endpoints when they appear at the start of a route (for example, if they appear in the
from()DSL command). That is, these endpoints can be configured to behave as a transactional client and they can also access a transactional resource.