shashank created CAMEL-25724:
--------------------------------

             Summary: camel-jms - with preserveMessageQos=true a message 
without JMSExpiration is sent with no time to live, ignoring the endpoint 
timeToLive and, for request/reply, the requestTimeout, so stale messages never 
expire
                 Key: CAMEL-25724
                 URL: https://issues.apache.org/jira/browse/CAMEL-25724
             Project: Camel
          Issue Type: Bug
          Components: camel-jms
            Reporter: shashank
            Assignee: shashank


The {{preserveMessageQos}} option is documented as: "The following three 
headers are considered JMSPriority, JMSDeliveryMode, and JMSExpiration. You can 
provide all or only some of them. If not provided, Camel will fall back to use 
the values from the endpoint instead."

{{JmsConfiguration.CamelJmsTemplate.doSend}} (main e339a2e9019f) does that for 
the priority, but not for the time to live:

{code:java}
if (config.isPreserveMessageQos()) {
    long ttl = message.getJMSExpiration();
    if (ttl != 0) {
        ttl = ttl - System.currentTimeMillis();
        if (ttl <= 0) {
            ttl = 1;
        }
    }
    int priority = message.getJMSPriority();
    if (priority < 0 || priority > 9) {
        // use priority from endpoint if not provided on a message with a valid 
range
        priority = this.getPriority();
    }
    ...
    producer.send(message, deliveryMode, priority, ttl);
{code}

A message without a {{JMSExpiration}} header has expiration 0, so it is sent 
with time to live 0, which in JMS means "never expires". The time to live of 
the template is never used on this path:
* InOnly: {{to("jms:queue:orders?preserveMessageQos=true&timeToLive=60000")}} 
sends messages that never expire.
* InOut: {{createInOutTemplate}} sets the {{requestTimeout}} (default 20 s) as 
time to live of the template ("About time to live" in the component page), but 
with {{preserveMessageQos=true}} the requests are sent without it. A request 
whose caller already timed out stays on the queue and is processed later, and 
requests pile up when the server is down.

There is no error and no log. Messages that come from another JMS endpoint 
usually carry their own {{JMSExpiration}}; the defect hits messages created by 
Camel (from a file, HTTP, timer, ...) and the typical reason to use the option, 
setting only a {{JMSPriority}} header.

Trigger: {{preserveMessageQos=true}} and a message without {{JMSExpiration}} 
(or 0), with {{timeToLive}} set (InOnly) or with request/reply (any 
{{requestTimeout}} > 0, the default) and {{disableTimeToLive=false}} (the 
default).

h3. Reproduction

New test {{JmsPreserveMessageQosTimeToLiveTest}} (6 tests, embedded Artemis and 
a mocked {{MessageProducer}}). Three fail on main:
* {{jms:queue:qos?preserveMessageQos=true&timeToLive=5000}}, message without 
expiration: {{MessageProducer.send}} gets time to live 0, expected 5000;
* InOut template of {{jms:queue:qos?preserveMessageQos=true}} (default 
requestTimeout 20000): time to live 0, expected 20000;
* a route 
{{from("direct:start").to("activemq:queue:X?preserveMessageQos=true&timeToLive=600000")}}
 on the embedded broker: the received message has {{JMSExpiration}} 0, expected 
about now + 10 minutes.
Three more pass on main and guard the parts that must not change: a message 
with an expiration keeps its remaining time; {{disableTimeToLive=true}} sends 
0; an endpoint without timeToLive sends 0.

A Z3 model of the chain (template time to live, header, {{exp - now}} with the 
{{<= 0}} clamp, 64-bit) gives the counterexamples above and shows that the fix 
meets the documented fallback for every input in realistic ranges, and changes 
nothing when the message has an expiration or the InOnly endpoint has no time 
to live.

h3. Proposed fix

When the message has no expiration, use the time to live of the template 
({{this.getTimeToLive()}}), which is the endpoint {{timeToLive}} (InOnly), 
{{timeToLive}}/{{requestTimeout}} (InOut), and 0 when none is configured or 
{{disableTimeToLive=true}}. Four lines in {{doSend}}; the camel-jms tests pass 
with it (452 + 16 + 6 run in the three surefire executions, 0 failures, 0 
errors, 11 skipped). Note for the 4.24 upgrade guide: such messages now expire; 
{{disableTimeToLive=true}} keeps the old behaviour. A message bridged from 
another JMS endpoint with expiration 0 also gets the endpoint value, because 
the producer cannot tell a 0 header from a missing one (the priority has the 
same limit).

Same code, not changed here: camel-sjms {{SjmsTemplate.send}} (the 
{{preserveMessageQos}} branch ignores the endpoint {{timeToLive}} the same way).

Affected: main (tested); camel-4.23.x, camel-4.22.x, camel-4.18.x and 
camel-4.14.x have the same lines (read, not run).

Duplicate check (2026-10-17): JIRA text "preserveMessageQos timeToLive", 
"preserveMessageQos JMSExpiration", "requestTimeout header time to live", 
summaries with "time to live": CAMEL-3743, CAMEL-4124, CAMEL-17912 (delivery 
mode and priority), CAMEL-7026 (request timeout header), none about this. 
GitHub pull requests "preserveMessageQos", "camel-jms timeToLive": none about 
this.

_Filed with Claude Code on behalf of allthingssecurity._




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

Reply via email to