Show Table of Contents
3.3. InOut Message Exchange Pattern
Combining InOut mode with transactional JMS endpoints is problematic. In most cases, this mode of operation is fundamentally inconsistent and it is recommended that you refactor your routes to avoid this combination.
Enabling InOut mode in JMS
In a JMS consumer endpoint, InOut mode is automatically triggered by the presence of a
JMSReplyToheader in an incoming JMS message. In this case, the endpoint creates an InOut exchange to hold the incoming message and it will use the
JMSReplyToqueue to send the reply message.
Problems combining InOut mode with transactions
The InOut MEP is fundamentally incompatible with a route containing transactional JMS endpoints. In almost all cases, the route will hang and no reply will ever be sent. To understand why, consider the following route for processing payment requests:
from("jmstx:queue:rawPayments") .process(inputReformatter) .to("jmstx:queue:formattedPayments") .process(outputReformatter);
The JMS consumer endpoint,
jmstx:queue:rawPayments, polls for messages, which are expected to have a
JMSReplyToheader (for InOut mode). For each incoming message, a new transaction is started and an InOut exchange is created. After reformatting by the
inputReformatterprocessor, the InOut exchange proceeds to the JMS producer endpoint,
jmstx:queue:formattedPayments, which sends the message and expects to receive a reply on a temporary queue. This scenario is illustrated by Figure 3.2, “Transactional JMS Route that Processes InOut Exchanges”
Figure 3.2. Transactional JMS Route that Processes InOut Exchanges
The scope of the transaction includes the entire route, the request leg as well as the reply leg. The processing of the route proceeds as expected until the exchange arrives at the JMS producer endpoint, at which point the producer endpoint makes a provisional write to the outgoing request queue. At this point the route hangs: the JMS producer endpoint is waiting to receive a message from the reply queue, but the reply can never be received because the outgoing request message was only provisionally written to the request queue (and is thus invisible to the service at the other end of the queue).
It turns out that this problem is not trivial to solve. When you consider all of the ways that this scenario could fail and how to guarantee transactional integrity in all cases, it would require some substantial changes to the way that Apache Camel works. Fortunately, there is a simpler way of dealing with request/reply semantics that is already supported by Apache Camel.
Refactoring routes to avoid InOut mode
If you want to implement a transactional JMS route that has request/reply semantics, the easiest solution is to refactor your route to avoid using InOut exchanges. The basic idea is that instead of defining a single route that combines a request leg and a reply leg, you should refactor it into two routes: one for the (outbound) request leg and another for the (inbound) reply leg. For example, the payments example could be refactored into two separate routes as follows:
from("jmstx:queue:rawPaymentsIn") .process(inputReformatter) .to("jmstx:queue:formattedPaymentsIn"); from("jmstx:queue:formattedPaymentsOut") .process(outputReformatter) .to("jmstx:queue:rawPaymentsOut");
Instead of a single incoming queue,
queue:rawPayments, which uses the queue from
JMSReplyTofor replies, we now have a pair of queues:
queue:rawPaymentsIn, for receiving incoming requests, and
queue:formattedPaymentsOut, for sending outgoing replies. Instead of a single outgoing queue,
queue:formattedPayments, which implicitly uses a temporary queue for replies, we now have a pair of queues:
queue:formattedPaymentsOut, for forwarding outgoing requests, and
queue:formattedPaymentsIn, for receiving incoming replies. This scenario is illustrated by Figure 3.3, “Pair of Transactional JMS Routes that Support Request/Reply Semantics”.
Figure 3.3. Pair of Transactional JMS Routes that Support Request/Reply Semantics
A special case
There is a special case of a transactional JMS route where you can process InOut exchanges. If you look at the preceding examples, it is clear that the essential cause of deadlock in the route is the presence of JMS producer endpoints that obey request/reply semantics. In contrast to this, if you define a route where the JMS producer endpoints obey oneway semantics (fire-and-forget), deadlock does not occur.
For example, if you want to have a route that records all of the processed exchanges in a log queue,
queue:log, you could define a route like the following:
from("jmstx:queue:inOutSource") .to(ExchangePattern.InOnly, "jmstx:queue:log") .process(myProcessor);
The exchanges coming into this route are of InOut type and both the consumer endpoint,
jmstx:queue:inOutSource, and the producer endpoint,
jmstx:queue:log, are transactional. The key to avoiding deadlock in this case is to force the producer endpoint to operate in oneway mode, by passing the
ExchangePattern.InOnlyparameter to the