This is an automated email from the ASF dual-hosted git repository.
Croway pushed a commit to branch camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.18.x by this push:
new d0d6d478ff36 [backport camel-4.18.x] CAMEL-24187: camel-core -
CamelContext.getEndpoint() never caches a uri containing a % character
d0d6d478ff36 is described below
commit d0d6d478ff365119f316723951b1602b49941136
Author: Croway <[email protected]>
AuthorDate: Thu Jul 16 18:02:03 2026 +0200
[backport camel-4.18.x] CAMEL-24187: camel-core -
CamelContext.getEndpoint() never caches a uri containing a % character
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
.../camel/impl/engine/AbstractCamelContext.java | 38 +++++++++++++++++++---
.../camel/impl/DefaultEndpointRegistryTest.java | 26 +++++++++++++++
2 files changed, 59 insertions(+), 5 deletions(-)
diff --git
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
index e0c5a77a35c9..753bc560dbb3 100644
---
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
+++
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
@@ -860,8 +860,8 @@ public abstract class AbstractCamelContext extends
BaseService
if (answer != null) {
if (!prototype) {
addService(answer);
- // register in registry
- answer = addEndpointToRegistry(uri, answer);
+ // register in registry (uri is already normalized
above, so avoid normalizing it again)
+ answer = addEndpointToRegistry(uri, answer, true);
} else {
addPrototypeService(answer);
// if there is endpoint strategies, then use the
endpoints they return
@@ -912,6 +912,21 @@ public abstract class AbstractCamelContext extends
BaseService
* @return the added endpoint
*/
protected Endpoint addEndpointToRegistry(String uri, Endpoint endpoint) {
+ return addEndpointToRegistry(uri, endpoint, false);
+ }
+
+ /**
+ * Strategy to add the given endpoint to the internal endpoint registry
+ *
+ * @param uri uri of the endpoint
+ * @param endpoint the endpoint to add
+ * @param normalized whether the uri is already normalized (for example
because it was normalized earlier to do the
+ * cache lookup that preceded this call).
Re-normalizing an already normalized uri is not
+ * idempotent for uris containing a literal {@code %}
character, so callers must not normalize
+ * twice or the stored key will never match a later
lookup key (CAMEL-24171).
+ * @return the added endpoint
+ */
+ protected Endpoint addEndpointToRegistry(String uri, Endpoint endpoint,
boolean normalized) {
StringHelper.notEmpty(uri, "uri");
ObjectHelper.notNull(endpoint, "endpoint");
@@ -920,7 +935,7 @@ public abstract class AbstractCamelContext extends
BaseService
for (EndpointStrategy strategy : getEndpointStrategies()) {
endpoint = strategy.registerEndpoint(uri, endpoint);
}
- endpoints.put(getEndpointKey(uri, endpoint), endpoint);
+ endpoints.put(getEndpointKey(uri, endpoint, normalized), endpoint);
return endpoint;
}
@@ -942,11 +957,24 @@ public abstract class AbstractCamelContext extends
BaseService
* @return the key
*/
protected NormalizedUri getEndpointKey(String uri, Endpoint endpoint) {
+ return getEndpointKey(uri, endpoint, false);
+ }
+
+ /**
+ * Gets the endpoint key to use for lookup or whe adding endpoints to the
{@link DefaultEndpointRegistry}
+ *
+ * @param uri the endpoint uri
+ * @param endpoint the endpoint
+ * @param normalized whether the uri is already normalized; see
+ * {@link #addEndpointToRegistry(String, Endpoint,
boolean)}
+ * @return the key
+ */
+ protected NormalizedUri getEndpointKey(String uri, Endpoint endpoint,
boolean normalized) {
if (endpoint != null && !endpoint.isSingleton()) {
int counter = endpointKeyCounter.incrementAndGet();
- return NormalizedUri.newNormalizedUri(uri + ":" + counter, false);
+ return NormalizedUri.newNormalizedUri(uri + ":" + counter,
normalized);
} else {
- return NormalizedUri.newNormalizedUri(uri, false);
+ return NormalizedUri.newNormalizedUri(uri, normalized);
}
}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/impl/DefaultEndpointRegistryTest.java
b/core/camel-core/src/test/java/org/apache/camel/impl/DefaultEndpointRegistryTest.java
index ccbb1604e32f..7511dd42efa3 100644
---
a/core/camel-core/src/test/java/org/apache/camel/impl/DefaultEndpointRegistryTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/impl/DefaultEndpointRegistryTest.java
@@ -102,6 +102,32 @@ public class DefaultEndpointRegistryTest {
ctx.stop();
}
+ // Testing the issue https://issues.apache.org/jira/browse/CAMEL-24171
+ @Test
+ public void testGetEndpointIsCachedForUriContainingPercentCharacter() {
+ DefaultCamelContext ctx = new DefaultCamelContext();
+ ctx.start();
+
+ // A % anywhere in the query string makes
EndpointHelper.normalizeEndpointUri() non-idempotent:
+ // running it twice turns %41 into %2541 instead of leaving it alone.
doGetEndpoint() normalizes
+ // the uri once for the cache lookup
(NormalizedUri.newNormalizedUri(uri, true) - "already normalized").
+ // But on a cache miss it hands that *already normalized* uri to
addEndpointToRegistry(), which calls
+ // getEndpointKey(uri, endpoint) ->
NormalizedUri.newNormalizedUri(uri, false) - "not normalized yet,
+ // please normalize" - normalizing it a second time before storing it.
The stored key (double
+ // normalized) then never matches the lookup key computed on a later
call (single normalized), so a
+ // brand-new Endpoint (with its own doStart()) gets created on every
single call instead of the
+ // cached singleton being reused.
+ String uri = "controlbus:route?routeId=RAW(%41)&action=status";
+
+ Endpoint first = ctx.getEndpoint(uri);
+ Endpoint second = ctx.getEndpoint(uri);
+
+ Assertions.assertSame(first, second,
+ "getEndpoint(uri) must return the cached singleton instance on
a second call with the identical uri");
+ Assertions.assertEquals(1, ctx.getEndpoints().size(),
+ "the endpoint registry must not accumulate a new entry on
every call for the same uri");
+ }
+
@Test
public void testMigration() {
DefaultCamelContext ctx = new DefaultCamelContext();