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)