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();

Reply via email to