[
https://issues.apache.org/jira/browse/CAMEL-24187?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Federico Mariani resolved CAMEL-24187.
--------------------------------------
Resolution: Fixed
> 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
> Fix For: 4.18.4, 4.22.0
>
>
> 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)