shashank created CAMEL-25253:
--------------------------------

             Summary: camel-dynamic-router - updating a subscription is ignored 
when the priority is unchanged, and duplicates the subscription when the 
priority changes
                 Key: CAMEL-25253
                 URL: https://issues.apache.org/jira/browse/CAMEL-25253
             Project: Camel
          Issue Type: Bug
          Components: camel-core
            Reporter: shashank


The control endpoint and the JMX operations can update an existing subscription 
({{update=true}}: "update the subscription (true), or add a new one (false)"). 
{{DynamicRouterFilterService.addFilterForChannel}} checks that a filter with 
the id exists and then only does

{code:java}
filters.add(filter);                         // ConcurrentSkipListSet ordered 
by FILTER_COMPARATOR (priority, then id)
filterStatistics.add(filter.statistics());
{code}

The set compares filters by priority and id only, so:

* *same priority:* the new filter is "equal" to the existing one, {{add}} 
returns false and the old filter stays. The update (new predicate, new 
destination) is silently ignored, while the operation returns the subscription 
id as if it had succeeded;
* *other priority:* both filters stay in the set. The subscription now routes 
with its old and its new definition: in {{allMatch}} mode messages go to both 
destinations (duplicates), in {{firstMatch}} mode the old definition can still 
win; and {{getFilterById}} returns either.

In addition, {{filterExists}} is computed with 
{{filterMap.get(channel).stream()}} as soon as any channel has filters, so a 
subscription for a channel that has none yet fails with a 
{{NullPointerException}}.

The existing unit test of the update passes only because its factory returns 
the same mock for the old and the new filter, whose priority it changes in 
place.

h3. Reproduction

With the default filter factory:

{code:java}
DynamicRouterFilterService service = new DynamicRouterFilterService();
service.initializeChannelFilters("test");
service.addFilterForChannel("sub", 1, PredicateBuilder.constant(true), 
"mock:old", "test", false);
service.addFilterForChannel("sub", 1, PredicateBuilder.constant(true), 
"mock:new", "test", true);   // returns "sub"
service.getFilterById("sub", "test").endpoint();                                
                    // "mock:old"

service.addFilterForChannel("sub", 10, PredicateBuilder.constant(true), 
"mock:new", "test", true);
service.getFiltersForChannel("test").size();                                    
                    // 2

service.addFilterForChannel("other", 1, PredicateBuilder.constant(true), 
"mock:other", "other", false); // NullPointerException
{code}

A unit test with these three cases fails on main.

h3. Proposed fix

Get (or create) the channel's filter set and statistics list first; on an 
update remove the existing filter with that id, then add the new filter. The 
statistics of the replaced filter stay in the list, as {{removeFilterById}} 
keeps those of a removed filter on purpose ("the statistics still represent 
actions that happened"), so after an update the list looks as after an 
unsubscribe and a subscribe with the same id. With the fix the new test and the 
camel-dynamic-router suite pass (137 unit tests, 21 integration tests).

Affected: 4.14.x, 4.18.x and main (same code since CAMEL-20302, 4.4).

Duplicate check (2026-10-01): JIRA text "dynamic-router" (54 issues) and 
"dynamic router" with "update": CAMEL-21407 (unsubscribe), CAMEL-23735 
(aggregation), CAMEL-24576 (control predicate), none about update. GitHub pull 
requests: none open on the component.

_Filed with Claude Code on behalf of allthingssecurity._




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

Reply via email to