shashank created CAMEL-25122:
--------------------------------
Summary: camel-sjms - InOut: when the send fails, the request
timeout completes the exchange a second time (the reply handler is registered
before the send and never cancelled)
Key: CAMEL-25122
URL: https://issues.apache.org/jira/browse/CAMEL-25122
Project: Camel
Issue Type: Bug
Components: camel-sjms, camel-sjms2
Reporter: shashank
{{SjmsProducer.processInOut}} registers the reply handler in the correlation
map inside {{MessageCreator.createMessage}}
({{replyManager.registerReply(...)}}), which runs before the message is sent.
When the send fails, the producer sets the exception and calls
{{callback.done(true)}}, but the handler stays registered. When the request
timeout expires, the timeout checker evicts it, and
{{ReplyManagerSupport.processReply}} completes the same exchange a second time:
it replaces the exception with an {{ExchangeTimedOutException}} and calls
{{callback.done(false)}}.
This is the bug that CAMEL-24073 fixed in camel-jms (4.22.0). camel-sjms has
the same code and was not changed; its {{ReplyManager}} has no way to cancel a
pending reply.
Two more cases complete the exchange twice, the other way around (the same as
CAMEL-25095 for camel-jms):
* the send blocks longer than {{requestTimeout}} and then fails: the timeout
completes the exchange first, then the send failure completes it again;
* the request reaches the broker and is answered, and then the send reports a
failure (a lost acknowledgement): the reply completes the exchange first, then
the send failure.
Reproduced with a unit test in camel-sjms (embedded Artemis; the connection
factory is wrapped so that the send to one queue fails, no Camel code changed),
InOut to {{sjms:queue:...}}:
* the send fails at once, {{requestTimeout=500}}: the exchange fails with the
send failure, and about 500 ms later its exception is replaced by
{{ExchangeTimedOutException}} (the second completion).
* the send blocks until the request timeout has completed the exchange, then
fails: the caller gets the send failure instead of the
{{ExchangeTimedOutException}} that completed the exchange.
* the reply arrives while the send is still running, then the send fails: the
caller gets the send failure although the reply had completed the exchange.
A second completion runs the on completions of the exchange a second time (such
as a consumer's commit and rollback), and runs error handling on a completed
exchange (for camel-jms, CAMEL-25095 also showed a negative inflight count; not
measured here).
h3. Proposed fix
As in camel-jms:
* {{ReplyManager}} gets {{boolean cancelCorrelationId(String correlationId)}},
implemented by {{ReplyManagerSupport}}: it removes the pending reply and
returns whether it was still pending.
* When the send fails, {{processInOut}} cancels the correlation id it
registered. If it was still pending, the exchange fails with the send failure
as before (and the timeout no longer fires). If the request timeout or the
reply has already removed it, they complete the exchange, so the send failure
is only logged at WARN, and {{processInOut}} returns without completing the
exchange.
With the fix, the three cases above complete the exchange once, with the send
failure, the {{ExchangeTimedOutException}} and the reply respectively. The new
method on {{ReplyManager}} and the changed outcome of a late send failure need
an upgrade guide note.
Affected: all versions of camel-sjms, and camel-sjms2, which uses the
camel-sjms producer (and gets the fix too).
Duplicate check (2026-09-29): JIRA component camel-sjms since June 2025 (15
issues: flaky tests, CAMEL-24083 asyncConsumer, CAMEL-23646, ObjectMessage and
header filtering: nothing on this), text "sjms" with "timeout" and "twice":
nothing. CAMEL-24073 and CAMEL-25095 are camel-jms only. GitHub pull requests
"SjmsProducer", "sjms cancelCorrelationId": nothing on this.
_Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)