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.
In a JMS consumer endpoint, InOut mode is automatically
triggered by the presence of a JMSReplyTo
header in an incoming JMS
message. In this case, the endpoint creates an InOut exchange
to hold the incoming message and it will use the JMSReplyTo
queue to
send the reply message.
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 JMSReplyTo
header (for
InOut mode). For each incoming message, a new transaction
is started and an InOut exchange is created. After reformatting
by the inputReformatter
processor, 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
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 Fuse Mediation Router works. Fortunately, there is a simpler way of dealing with request/reply semantics that is already supported by Fuse Mediation Router.
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 JMSReplyTo
for 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.
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.InOnly
parameter to the
to()
command,