This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch did
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 7fe4e2ae35804a21754f82144f16a4cd4a301e4b
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Sep 20 11:35:42 2025 +0200

    CAMEL-22420: camel-core - Detect duplicate processor ids within same route
---
 .../camel/model/ProcessorDefinitionHelper.java     | 12 +++++------
 .../apache/camel/model/RouteDefinitionHelper.java  | 22 ++++++++++++++------
 .../camel/management/ManagedDuplicateIdTest.java   | 24 ++++++++++++++++++++++
 3 files changed, 45 insertions(+), 13 deletions(-)

diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java
index 1dd53f99079..1372486a803 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java
@@ -18,9 +18,7 @@ package org.apache.camel.model;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Set;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.NamedNode;
@@ -180,15 +178,15 @@ public final class ProcessorDefinitionHelper {
      * Traverses the node, including its children (recursive), and gathers all 
the node ids.
      *
      * @param  node            the target node
-     * @param  set             set to store ids, if <tt>null</tt> a new set 
will be created
+     * @param  set             list to store ids, if <tt>null</tt> a new list 
will be created
      * @param  onlyCustomId    whether to only store custom assigned ids (ie.
      *                         {@link 
org.apache.camel.model.OptionalIdentifiedDefinition#hasCustomIdAssigned()}
      * @param  includeAbstract whether to include abstract nodes (ie.
      *                         {@link 
org.apache.camel.model.ProcessorDefinition#isAbstract()}
-     * @return                 the set with the found ids.
+     * @return                 the list with the found ids.
      */
-    public static Set<String> gatherAllNodeIds(
-            ProcessorDefinition<?> node, Set<String> set, boolean 
onlyCustomId, boolean includeAbstract) {
+    public static List<String> gatherAllNodeIds(
+            ProcessorDefinition<?> node, List<String> set, boolean 
onlyCustomId, boolean includeAbstract) {
         if (node == null) {
             return set;
         }
@@ -199,7 +197,7 @@ public final class ProcessorDefinitionHelper {
         }
 
         if (set == null) {
-            set = new LinkedHashSet<>();
+            set = new ArrayList<>();
         }
 
         // add ourselves
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
index da1748462db..8645a0d35db 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
@@ -22,11 +22,14 @@ import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.ErrorHandlerFactory;
@@ -260,11 +263,9 @@ public final class RouteDefinitionHelper {
      * @return          <tt>null</tt> if no duplicate id's detected, otherwise 
the first found duplicate id is returned.
      */
     public static String validateUniqueIds(RouteDefinition target, 
List<RouteDefinition> routes, String prefixId) {
-        Set<String> routesIds = new LinkedHashSet<>();
-        // gather all ids for the existing route, but only include custom ids,
-        // and no abstract ids
-        // as abstract nodes is cross-cutting functionality such as 
interceptors
-        // etc
+        List<String> routesIds = new ArrayList<>();
+        // gather all ids for the existing route, but only include custom ids, 
and no abstract ids
+        // as abstract nodes is cross-cutting functionality such as 
interceptors etc
         for (RouteDefinition route : routes) {
             // skip target route as we gather ids in a separate set
             if (route == target) {
@@ -275,8 +276,17 @@ public final class RouteDefinitionHelper {
 
         // gather all ids for the target route, but only include custom ids, 
and
         // no abstract ids as abstract nodes is cross-cutting functionality 
such as interceptors etc
-        Set<String> targetIds = new LinkedHashSet<>();
+        List<String> targetIds = new ArrayList<>();
         ProcessorDefinitionHelper.gatherAllNodeIds(target, targetIds, true, 
false);
+        // are there any duplicates processor ids in the target route
+        List<String> duplicates = 
targetIds.stream().collect(Collectors.groupingBy(Function.identity()))
+                .entrySet().stream()
+                .filter(e -> e.getValue().size() > 1)
+                .map(Map.Entry::getKey)
+                .toList();
+        if (!duplicates.isEmpty()) {
+            return duplicates.get(0);
+        }
 
         // now check for clash with the target route
         for (String id : targetIds) {
diff --git 
a/core/camel-management/src/test/java/org/apache/camel/management/ManagedDuplicateIdTest.java
 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedDuplicateIdTest.java
index 22dc06931eb..f5b4a847724 100644
--- 
a/core/camel-management/src/test/java/org/apache/camel/management/ManagedDuplicateIdTest.java
+++ 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedDuplicateIdTest.java
@@ -57,6 +57,30 @@ public class ManagedDuplicateIdTest extends 
ManagementTestSupport {
         }
     }
 
+    @Test
+    public void testDuplicateIdSingleRoute() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:foo").routeId("foo")
+                        .to("log:line").id("clash")
+                        .to("log:foo").id("cheese")
+                        .split(body()).id("mysplit")
+                        .to("log:line").id("clash")
+                        .end()
+                        .to("mock:foo");
+            }
+        });
+        try {
+            context.start();
+            fail("Should fail");
+        } catch (Exception e) {
+            assertEquals(
+                    "Failed to start route: foo because: Duplicate id 
detected: clash. Please correct ids to be unique among all your routes.",
+                    e.getMessage());
+        }
+    }
+
     @Override
     public boolean isUseRouteBuilder() {
         return false;

Reply via email to