Federico Mariani created CAMEL-24187:
----------------------------------------

             Summary: 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


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