shashank created CAMEL-25252:
--------------------------------

             Summary: camel-timer - stopping a timer consumer with a negative 
delay releases the shared java.util.Timer it never took, which cancels the 
timer of the other routes with the same timer name (they stay Started but never 
fire again)
                 Key: CAMEL-25252
                 URL: https://issues.apache.org/jira/browse/CAMEL-25252
             Project: Camel
          Issue Type: Bug
          Components: camel-core
            Reporter: shashank


The timer consumers of the same timer name share one {{java.util.Timer}}, which 
{{TimerComponent}} reference counts: {{getTimer}} creates it or increments the 
count (line numbers of main, :63-79), {{removeTimer}} decrements it and cancels 
the {{Timer}} at 0 (:81-92).

{{TimerConsumer}} takes the reference only for a delay of 0 or more 
({{doStart}} :181-186, or {{onCamelContextStarted}} :229-234 when the consumer 
was started before the context). A consumer with a negative delay 
({{delay=-1}}, fire as fast as possible, CAMEL-9249) uses its own single thread 
executor (:190) and never calls {{getTimer}}. But {{doStop}} always calls 
{{endpoint.removeTimer(this)}} (:217). So stopping such a consumer decrements a 
count it never incremented. When it was the last reference of another consumer, 
the shared {{Timer}} is cancelled while that consumer is still running: its 
route stays Started but never fires again, and nothing is logged. Because the 
holder is removed from the map, a route started later on the same name gets a 
new {{Timer}}, but the running one is not rescheduled.

Example: {{from("timer:tick?period=1000")}} in one route and 
{{from("timer:tick?delay=-1&repeatCount=1")}} in another (a one-shot start-up 
route). Stopping the second route (JMX, the route controller, a route policy, 
or a restart of it) silently stops the first.

h3. Reproduction

Route A {{timer:shared?period=20}}, route B 
{{timer:shared?delay=-1&repeatCount=1}}; wait until both fired, stop route B, 
then wait up to 3 s for 5 more exchanges from A: none arrive, A reports 
Started. 3 of 3 runs, and 10 of 10 in a loop; the same after B was started and 
stopped twice. Control: B {{timer:shared?delay=0&repeatCount=1}} (takes the 
timer): A keeps firing (3 of 3).

A TLA+ model of the reference count with consumers that do and do not take the 
timer, started and stopped in any order, violates "every started consumer with 
a delay of 0 or more is scheduled on a Timer that is not cancelled" and "the 
count equals the consumers scheduled on the Timer" as soon as a negative-delay 
consumer is stopped; with the fix both hold (2 and 3 consumers, up to 9 
start/stop operations); with only non-negative delays they hold on the current 
code.

h3. Proposed fix

{{TimerConsumer}} remembers whether it took the timer (a flag set where 
{{getTimer}} is called in {{doStart}} and {{onCamelContextStarted}}) and 
{{doStop}} calls {{removeTimer}} only then, clearing the flag. It would also 
cover a consumer with a delay of 0 or more that is stopped before the 
CamelContext completed its start, which has not taken the timer yet either (not 
tested). With the fix route A keeps firing after B is stopped once or twice (3 
of 3), the control is unchanged, and the timer tests of camel-core pass (19 
classes, 34 tests, including {{TimerMultipleConsumerStopRouteTest}}, 
{{TimerNegativeDelayTest}}, {{TimerRestartTest}} and a new stop/restart test of 
a negative-delay route next to a period route on the same timer name, which 
fails without the fix).

Affected: all versions with delay=-1 (the same code at camel-3.20.0, 4.0.0, 
4.10.0, 4.14.0, 4.18.0, 4.22.0 and main).

Duplicate check (2026-09-30): JIRA text "TimerComponent" (12, the newest 
CAMEL-18232 thread name pattern), "timer" with "delay=-1" (CAMEL-9249), 
component camel-timer (none recent); GitHub pull requests "camel-timer", "timer 
removeTimer", "timer delay=-1": 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