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

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


The following commit(s) were added to refs/heads/main by this push:
     new ec7354b3d3f7 CAMEL-23735: Restore documented default aggregation 
strategy in dynamic-router
ec7354b3d3f7 is described below

commit ec7354b3d3f7ff4b1097212cba16c184bb0e557a
Author: Tholgue <[email protected]>
AuthorDate: Thu Jun 11 13:29:15 2026 +0200

    CAMEL-23735: Restore documented default aggregation strategy in 
dynamic-router
    
    The CAMEL-20302 refactoring (4.4.0) unintentionally changed the default
    aggregation strategy from UseLatestAggregationStrategy to a
    NoopAggregationStrategy that keeps the first reply, contradicting the
    documented behavior ("Camel will use the last reply as the outgoing
    message"). This restores UseLatestAggregationStrategy as the default,
    deprecates NoopAggregationStrategy for binary compatibility, and adds
    an integration test reproducing the issue.
    
    Closes #23949
---
 .../routing/DynamicRouterRecipientListHelper.java  |  11 +-
 .../DynamicRouterDefaultAggregationIT.java         | 121 +++++++++++++++++++++
 .../DynamicRouterRecipientListHelperTest.java      |   5 +-
 3 files changed, 134 insertions(+), 3 deletions(-)

diff --git 
a/components/camel-dynamic-router/src/main/java/org/apache/camel/component/dynamicrouter/routing/DynamicRouterRecipientListHelper.java
 
b/components/camel-dynamic-router/src/main/java/org/apache/camel/component/dynamicrouter/routing/DynamicRouterRecipientListHelper.java
index 4d1df1c681f6..414a16814e07 100644
--- 
a/components/camel-dynamic-router/src/main/java/org/apache/camel/component/dynamicrouter/routing/DynamicRouterRecipientListHelper.java
+++ 
b/components/camel-dynamic-router/src/main/java/org/apache/camel/component/dynamicrouter/routing/DynamicRouterRecipientListHelper.java
@@ -152,7 +152,7 @@ public final class DynamicRouterRecipientListHelper {
                                 .orElseThrow(() -> new 
IllegalArgumentException(
                                         "Cannot find AggregationStrategy in 
Registry with name: " +
                                                                                
 cfg.getAggregationStrategy()))))
-                .orElse(new NoopAggregationStrategy());
+                .orElse(new UseLatestAggregationStrategy());
         CamelContextAware.trySetCamelContext(strategy, camelContext);
         return cfg.isShareUnitOfWork() ? new 
ShareUnitOfWorkAggregationStrategy(strategy) : strategy;
     }
@@ -256,6 +256,15 @@ public final class DynamicRouterRecipientListHelper {
                 .orElse(null);
     }
 
+    /**
+     * Keeps the first result and discards later ones. This was 
unintentionally used as the default aggregation
+     * strategy, contradicting the documented behavior of using the last reply 
as the outgoing message.
+     *
+     * @deprecated no longer used as the default aggregation strategy: {@link 
UseLatestAggregationStrategy} is used
+     *             instead, matching the documentation. Configure an explicit 
{@code aggregationStrategy} if the
+     *             first-result behavior is desired.
+     */
+    @Deprecated
     public static class NoopAggregationStrategy implements AggregationStrategy 
{
 
         public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
diff --git 
a/components/camel-dynamic-router/src/test/java/org/apache/camel/component/dynamicrouter/integration/DynamicRouterDefaultAggregationIT.java
 
b/components/camel-dynamic-router/src/test/java/org/apache/camel/component/dynamicrouter/integration/DynamicRouterDefaultAggregationIT.java
new file mode 100644
index 000000000000..bdf67dfc9ff5
--- /dev/null
+++ 
b/components/camel-dynamic-router/src/test/java/org/apache/camel/component/dynamicrouter/integration/DynamicRouterDefaultAggregationIT.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.dynamicrouter.integration;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import 
org.apache.camel.component.dynamicrouter.control.DynamicRouterControlMessage;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.infra.core.CamelContextExtension;
+import org.apache.camel.test.infra.core.DefaultCamelContextExtension;
+import org.apache.camel.test.infra.core.annotations.RouteFixture;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+/**
+ * Verifies the default aggregation behavior of the Dynamic Router: as 
documented, when no aggregationStrategy is
+ * configured, "Camel will use the last reply as the outgoing message", so 
modifications made by the last matching
+ * recipient (message body, exchange properties) must be visible in the 
calling route after the dynamic-router call.
+ */
+public class DynamicRouterDefaultAggregationIT {
+
+    @RegisterExtension
+    protected static CamelContextExtension contextExtension = new 
DefaultCamelContextExtension();
+
+    CamelContext context;
+
+    ProducerTemplate template;
+
+    MockEndpoint resultMock;
+
+    @RouteFixture
+    public void createRouteBuilder(CamelContext context) throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start")
+                        .routeId("directToDynamicRouter")
+                        .to("dynamic-router://test?recipientMode=allMatch")
+                        .to("mock:result");
+
+                // First recipient (priority 1): leaves its copy of the 
exchange untouched
+                from("direct:first")
+                        .routeId("firstRecipient")
+                        .log("first recipient received '${body}'");
+
+                // Second recipient (priority 2): modifies the body and sets a 
property on its copy
+                from("direct:second")
+                        .routeId("secondRecipient")
+                        .setProperty("enrichedBy", constant("second"))
+                        .setBody(constant("modified by second"));
+            }
+        });
+    }
+
+    @BeforeEach
+    void setUp() {
+        this.context = contextExtension.getContext();
+        this.template = context.createProducerTemplate();
+        this.resultMock = contextExtension.getMockEndpoint("mock:result", 
true);
+    }
+
+    @AfterEach
+    void tearDown() {
+        context.stop();
+    }
+
+    /**
+     * With two matching recipients in allMatch mode, the result that comes 
back to the calling route must be the last
+     * reply: the body and the exchange property set by the second recipient.
+     *
+     * @throws Exception if interrupted while waiting for mocks to be satisfied
+     */
+    @Test
+    void testDefaultAggregationUsesLastReply() throws Exception {
+        subscribe("firstSubscription", "direct:first", 1);
+        subscribe("secondSubscription", "direct:second", 2);
+
+        resultMock.expectedMessageCount(1);
+
+        template.sendBody("direct:start", "original");
+
+        MockEndpoint.assertIsSatisfied(context, 5, TimeUnit.SECONDS);
+
+        Exchange result = resultMock.getExchanges().get(0);
+        Assertions.assertEquals("modified by second", 
result.getMessage().getBody(String.class));
+        Assertions.assertEquals("second", result.getProperty("enrichedBy", 
String.class));
+    }
+
+    private void subscribe(String subscriptionId, String destinationUri, int 
priority) {
+        DynamicRouterControlMessage controlMessage = 
DynamicRouterControlMessage.Builder.newBuilder()
+                .subscribeChannel("test")
+                .subscriptionId(subscriptionId)
+                .destinationUri(destinationUri)
+                .priority(priority)
+                .predicate("${body} != null")
+                .expressionLanguage("simple")
+                .build();
+        template.sendBody("dynamic-router-control:subscribe", controlMessage);
+    }
+}
diff --git 
a/components/camel-dynamic-router/src/test/java/org/apache/camel/component/dynamicrouter/routing/DynamicRouterRecipientListHelperTest.java
 
b/components/camel-dynamic-router/src/test/java/org/apache/camel/component/dynamicrouter/routing/DynamicRouterRecipientListHelperTest.java
index 9cd622a9782f..7f9171d5f797 100644
--- 
a/components/camel-dynamic-router/src/test/java/org/apache/camel/component/dynamicrouter/routing/DynamicRouterRecipientListHelperTest.java
+++ 
b/components/camel-dynamic-router/src/test/java/org/apache/camel/component/dynamicrouter/routing/DynamicRouterRecipientListHelperTest.java
@@ -28,6 +28,7 @@ import org.apache.camel.Processor;
 import org.apache.camel.processor.RecipientList;
 import org.apache.camel.processor.aggregate.AggregationStrategyBeanAdapter;
 import 
org.apache.camel.processor.aggregate.AggregationStrategyBiFunctionAdapter;
+import org.apache.camel.processor.aggregate.UseLatestAggregationStrategy;
 import org.apache.camel.spi.ExecutorServiceManager;
 import org.apache.camel.spi.Registry;
 import org.junit.jupiter.api.Assertions;
@@ -225,11 +226,11 @@ class DynamicRouterRecipientListHelperTest {
     }
 
     @Test
-    void testCreateAggregationStrategyNoOp() {
+    void testCreateAggregationStrategyDefaultsToUseLatest() {
         when(mockConfig.getAggregationStrategyBean()).thenReturn(null);
         when(mockConfig.getAggregationStrategy()).thenReturn(null);
         AggregationStrategy strategy = 
DynamicRouterRecipientListHelper.createAggregationStrategy(camelContext, 
mockConfig);
-        
Assertions.assertInstanceOf(DynamicRouterRecipientListHelper.NoopAggregationStrategy.class,
 strategy);
+        Assertions.assertInstanceOf(UseLatestAggregationStrategy.class, 
strategy);
     }
 
     @Test

Reply via email to