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.
Figure 5.2 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.
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 InOut Message Exchange Pattern.
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:giro
, jmstx:queue:credits
, and
jmstx:queue:debits
. If the transaction succeeds, the exchange is
permanently removed from the giro
queue and pushed on to the
credits
queue and the debits
queue; if the transaction
fails, the exchange does not get put on to the
credits
and debits
queues and the exchange is pushed
back on to the giro
queue (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 transacted
property is set to
true
to enable transaction demarcation for
InOnly exchanges. For the complete Spring XML configuration
of this component, see Example 3.1.
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>
The 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 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 TransactedPolicy
bean having a non-default
propagation policy is created in Spring XML (see 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 Fuse Mediation Router 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.