Federico Mariani created CAMEL-24073:
----------------------------------------

             Summary: camel-jms: InOut AsyncCallback.done() invoked twice when 
the JMS send fails after reply registration
                 Key: CAMEL-24073
                 URL: https://issues.apache.org/jira/browse/CAMEL-24073
             Project: Camel
          Issue Type: Bug
          Components: camel-jms
    Affects Versions: 4.21.0
            Reporter: Federico Mariani
         Attachments: JmsRequestReplySendFailureDoubleCallbackTest.java

h3. Problem
In {{JmsProducer.processInOut}}, {{replyManager.registerReply(...)}} runs 
inside {{MessageCreator.createMessage()}} (JmsProducer.java:243), i.e. *before* 
{{producer.send()}} (CamelJmsTemplate.doSendToDestination: createProducer -> 
createMessage -> doSend). If the send (or transacted commit) throws - broker 
quota, max message size, security error, connection dropped mid-send - the 
exception completes the exchange via {{exchange.setException(e); 
callback.done(true)}}, but the handler stays registered in the 
{{CorrelationTimeoutMap}}: there is no unregister/cancel API on 
{{ReplyManager}}.

At {{requestTimeout}} (default 20s) the eviction fires {{onTimeout}} -> 
{{ReplyManagerSupport.processReply}}, which sets an 
{{ExchangeTimedOutException}} on the *already-completed* exchange and invokes 
{{callback.done(false)}} a *second time* - re-driving routing 
continuation/error handling on a finished exchange. With {{requestTimeout=0}} 
(encoded as Integer.MAX_VALUE ms in CorrelationTimeoutMap) the handler + 
exchange leak for ~24.8 days instead. The same gap applies to the 
{{useMessageIDAsCorrelationID}} provisional registration.

h3. Failure scenario
Route {{from("direct:a").to("jms:queue:foo?requestTimeout=20000")}}; broker 
rejects the send (e.g. max-message-size). The exchange fails immediately with 
the JMS exception (correct), then 20s later the callback fires again with a 
timeout exception on the completed exchange.

h3. Suggested fix direction
An unregister/cancel API on {{ReplyManager}} invoked from the producer's 
failure path (and from the provisional-correlation path). Consider fixing the 
stop-path variant at the same time: {{ReplyManagerSupport.doStop}} clears the 
correlation map without completing pending handlers, so in-flight InOut 
exchanges hang until the graceful-shutdown timeout.

h3. Reproducer
Attached {{JmsRequestReplySendFailureDoubleCallbackTest.java}} (place in 
{{components/camel-jms/src/test/java/org/apache/camel/component/jms/}}). It 
simulates the broker-side send failure by proxying the ConnectionFactory so 
{{MessageProducer.send()}} throws, and asserts the AsyncCallback is invoked 
exactly once - it observes 2 invocations today. Uses the Artemis test-infra VM 
broker.

----
_This issue was researched and filed by Claude Code on behalf of [~croway] 
(GitHub: Croway), as part of a deep code review of camel-jms and camel-amqp. A 
failing JUnit reproducer is attached; it fails deterministically on current 
main (4.22.0-SNAPSHOT)._



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to