shashank created CAMEL-26536:
--------------------------------

             Summary: camel-jms - with asyncStopListener=true, a JmsConsumer 
stopped and started again ends up started without a running listener container
                 Key: CAMEL-26536
                 URL: https://issues.apache.org/jira/browse/CAMEL-26536
             Project: Camel
          Issue Type: Bug
          Components: camel-jms
            Reporter: shashank
            Assignee: shashank


Found with a TLA+ model of {{JmsConsumer}} with {{asyncStartListener}} / 
{{asyncStopListener}} (the lifecycle thread, the asynchronous stop task and the 
asynchronous start task on the component's pool), cross-checked with a SPIN 
model, then reproduced on the real classes.

With {{asyncStopListener=true}}, {{JmsConsumer.doStop}} submits 
{{stopAndDestroyListenerContainer()}} to the component's thread pool and 
returns, with {{listenerContainer}} still set. If the same consumer is started 
again before that task has run (route policies that stop and start the 
consumer, such as the leader-election policies {{HazelcastRoutePolicy}} and 
{{MasterRoutePolicy}} of camel-zookeeper-master, the JMX start/stop operations 
of the consumer, or code that calls {{stop()}} / {{start()}} on it), 
{{doStart}} sees the old listener container, does not create a new one, and 
initializes and starts it again. The stop task then reads the field, stops and 
destroys that container, and sets {{listenerContainer}} to null. The consumer 
reports Started but has no listener container and consumes nothing, with no 
error logged. If the stop task nulls the field while {{doStart}} is between its 
null check and {{afterPropertiesSet()}}, the start fails with a 
NullPointerException instead. The stop task also shuts down the private thread 
pool stored in {{executorService}}, which can be the pool of a listener 
container created by the new start.

Short trace of the model:
{noformat}
1 lifecycle : doStop  -> listenerContainer = C1, submit stop task          
(JmsConsumer:216-219)
2 lifecycle : doStart -> listenerContainer != null, keep C1                 
(:149)
3 lifecycle : C1.afterPropertiesSet(); C1.start(); initialized = true     
(:177-189)
4 stop task : listenerContainer.stop(); listenerContainer.destroy()       
(:195-196)  -> C1 not running
5 stop task : listenerContainer = null; initialized = false               
(:203-205)
  -> consumer Started, no listener container
{noformat}

With {{asyncStartListener=true}} as well, the start task works on whatever the 
field holds when it runs: it can start the container the stop task destroys, 
hit the null field (NPE logged as "ignored"), or initialize a newer container a 
second time.

The real-class test holds the tasks the consumer submits to the pool and runs 
them in a chosen order: stopping and starting the consumer with the stop task 
pending leaves {{getListenerContainer().isRunning()}} false and the next 
message is not consumed. Without hooks, 30 stop/start cycles of the consumer 
with the real pool (in-VM Artemis) ended 30 times without a running listener 
container (27 started without a listener, 3 starts failed with the NPE).

A route restart ({{restartRoute}}, {{stopRoute}} + {{startRoute}}) is not 
affected, as the route creates a new consumer when it starts again.

*Proposal:* {{doStop}} detaches the listener container under a lock of the 
consumer (the container, its private thread pool and the {{initialized}} flag 
are taken and cleared at once), and the stop task stops and destroys only the 
container (and pool) it was given, so a start that runs before the stop task 
creates a new listener container. {{doStart}} hands the container it created to 
the start task, which does nothing if the consumer no longer uses it, and after 
starting it checks again under the lock and stops and destroys it if the 
consumer was stopped meanwhile.

Affected: main, 4.23.x, 4.22.x (the same {{JmsConsumer}} code), 4.18.x, 4.14.x 
(the same bug; {{initialized}} is set in {{doStart}} there).

_Filed with Claude Code on behalf of allthingssecurity._



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to