[ 
https://issues.apache.org/jira/browse/CAMEL-24187?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Work on CAMEL-24187 started by Federico Mariani.
------------------------------------------------
> camel-core: CamelContext.getEndpoint() never caches a uri containing a % 
> character (double-normalization in AbstractCamelContext.getEndpointKey)
> ------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-24187
>                 URL: https://issues.apache.org/jira/browse/CAMEL-24187
>             Project: Camel
>          Issue Type: Bug
>            Reporter: Federico Mariani
>            Assignee: Federico Mariani
>            Priority: Major
>
> h3. Summary
> {{AbstractCamelContext.doGetEndpoint()}} normalizes the incoming uri once for 
> its cache lookup, but on a cache miss it hands that *already normalized* uri 
> to {{addEndpointToRegistry()}}, which re-normalizes it a *second* time before 
> storing the registry key. {{EndpointHelper.normalizeEndpointUri()}} (via 
> {{URISupport.normalizeUri}} / {{UnsafeUriCharactersEncoder}}) is not 
> idempotent for any uri containing a literal {{%}} character - it encodes it 
> to {{%25}} every time it runs, so a second pass turns {{%41}} into {{%2541}}.
> Because the stored key (double-normalized) never equals the lookup key 
> computed fresh on a later call (single-normalized), 
> {{CamelContext#getEndpoint(String)}} returns a brand-new {{Endpoint}} 
> instance - and starts it - on *every single call* for any uri containing a 
> {{%}}, instead of reusing the cached singleton. This applies to any endpoint 
> uri with a literal {{%}} anywhere in it (path or query), regardless of 
> component.
> h3. Reproduction
> {code:java}
> DefaultCamelContext ctx = new DefaultCamelContext();
> ctx.start();
> String uri = "controlbus:route?routeId=RAW(%41)&action=status";
> Endpoint first = ctx.getEndpoint(uri);
> Endpoint second = ctx.getEndpoint(uri);
> // FAILS: first != second, even though both getEndpointUri() print the 
> identical text
> assertSame(first, second);
> {code}
> A minimal, deterministic reproducer is attached as a patch adding 
> {{DefaultEndpointRegistryTest#testGetEndpointIsCachedForUriContainingPercentCharacter}}
>  in camel-core.
> h3. Root cause (exact call chain)
> * {{AbstractCamelContext.doGetEndpoint()}} normalizes the uri once (line 
> ~810: {{uri = EndpointHelper.normalizeEndpointUri(uri)}}), then builds the 
> *lookup* key as {{NormalizedUri.newNormalizedUri(uri, true)}} - {{true}} 
> meaning "already normalized, do not touch it again".
> * On a cache miss it calls {{addEndpointToRegistry(uri, answer)}} with that 
> same already-normalized {{uri}}.
> * {{addEndpointToRegistry()}} calls {{getEndpointKey(uri, endpoint)}}, which 
> calls {{NormalizedUri.newNormalizedUri(uri, false)}} - {{false}} meaning "not 
> normalized yet, please normalize it" - re-running 
> {{EndpointHelper.normalizeEndpointUri()}} on the already-normalized string.
> Since normalization is not idempotent for {{%}}, the *stored* key and the 
> *lookup* key computed on the next call diverge permanently.
> h3. Real-world impact - discovered via CAMEL-24171
> This was discovered while investigating CAMEL-24171 (camel-plc4x: 
> {{Plc4XEndpoint#doStart()}} runs on every poll cycle for a S7 PLC route but 
> not for a structurally identical Modbus route). S7 tag addresses 
> conventionally contain a literal {{%}} (e.g. {{RAW(%DB1.DBX0.0:BOOL)}}), 
> while Modbus addresses typically do not (e.g. {{RAW(coil:1)}}). A route using 
> {{pollEnrich}} against a plc4x endpoint therefore resolves a *new* 
> {{Plc4XEndpoint}} - reloading the whole {{DefaultPlcDriverManager}} - on 
> every single poll for S7, while Modbus correctly reuses one cached endpoint 
> for the life of the route. camel-plc4x itself is not at fault; the same 
> symptom would occur for any component whose endpoint uri legitimately 
> contains a {{%}} and is re-resolved by string on every use (as 
> {{pollEnrich}}/{{toD}}/{{recipientList}} do).
> h3. Fix
> Add a {{normalized}}-aware overload of 
> {{getEndpointKey}}/{{addEndpointToRegistry}} so only the {{doGetEndpoint}} 
> call site (whose uri is provably already normalized) skips the redundant 
> second normalization pass. The public {{hasEndpoint(String)}}, 
> {{addEndpoint(String, Endpoint)}} and {{removeEndpoints(String)}} APIs still 
> pass raw, caller-supplied uris and must keep normalizing exactly as before - 
> a blanket flip of the existing method would silently break those.
> Verified: full {{camel-core}} test suite passes with the fix; the two 
> isolating reproducer tests ({{DefaultEndpointRegistryTest}} in camel-core, 
> {{Plc4XPollEnrichDoStartReproducerTest}} in camel-plc4x) both go from red to 
> green.
> See also: CAMEL-24171 (the plc4x symptom this was found through).



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

Reply via email to