[ 
https://issues.apache.org/jira/browse/CAMEL-24073?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Claus Ibsen resolved CAMEL-24073.
---------------------------------
    Resolution: Fixed

> 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
>            Assignee: Claus Ibsen
>            Priority: Major
>              Labels: code-review
>             Fix For: 4.22.0
>
>         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