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 11a0917b1698 CAMEL-25045: camel-core - Java DSL builders: fix bugs 
found in a deep review (#26926)
11a0917b1698 is described below

commit 11a0917b16982692014585754e5fa68a78c9b4bd
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 28 09:08:22 2026 +0200

    CAMEL-25045: camel-core - Java DSL builders: fix bugs found in a deep 
review (#26926)
    
    - choice().note() no longer recurses into itself (StackOverflowError)
    - throttle(Expression, long correlationExpressionKey) uses the key
    - route configuration onException(e1, e2, e3) keeps the third exception
    - simple(text, resultType, pretty, trimResult) sets trimResult
    - endDoTry() from a block inside doCatch/doFinally returns the try
    - enrichWith(EndpointProducerBuilder) is dynamic as its sibling overloads
    - the type: prefix works in all bean(...) overloads
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../camel/builder/ExpressionClauseSupport.java     |   1 +
 .../apache/camel/model/ProcessorDefinition.java    |  55 ++++---
 .../ProcessorDefinitionBuilderEdgeCasesTest.java   | 174 +++++++++++++++++++++
 3 files changed, 202 insertions(+), 28 deletions(-)

diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
index 3ae3f8278f9c..9e95800abb37 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
@@ -849,6 +849,7 @@ public class ExpressionClauseSupport<T> implements 
ExpressionFactoryAware, Predi
         SimpleExpression expression = new SimpleExpression(text);
         expression.setResultType(resultType);
         expression.setPretty(Boolean.toString(pretty));
+        expression.setTrimResult(Boolean.toString(trimResult));
         expression(expression);
         return result;
     }
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
index 5acd659582cc..d3c8cb78b296 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
@@ -854,7 +854,7 @@ public abstract class ProcessorDefinition<Type extends 
ProcessorDefinition<Type>
                     p.note(note);
                 }
             } else {
-                cbr.note(note);
+                cbr.setNote(note);
             }
             return asType();
         }
@@ -1220,6 +1220,10 @@ public abstract class ProcessorDefinition<Type extends 
ProcessorDefinition<Type>
 
         // okay end this and get back to the try
         def = end();
+        if (def instanceof CatchDefinition || def instanceof 
FinallyDefinition) {
+            // we ended a block inside doCatch or doFinally, so go back to the 
try that owns it
+            def = def.getParent();
+        }
         return (TryDefinition) def;
     }
 
@@ -2101,7 +2105,8 @@ public abstract class ProcessorDefinition<Type extends 
ProcessorDefinition<Type>
      * @return                           the builder
      */
     public ThrottleDefinition throttle(Expression maximumConcurrentRequests, 
long correlationExpressionKey) {
-        ThrottleDefinition answer = new 
ThrottleDefinition(maximumConcurrentRequests, maximumConcurrentRequests);
+        ThrottleDefinition answer = new ThrottleDefinition(
+                maximumConcurrentRequests, new 
ConstantExpression(Long.toString(correlationExpressionKey)));
         addOutput(answer);
         return answer;
     }
@@ -2454,7 +2459,7 @@ public abstract class ProcessorDefinition<Type extends 
ProcessorDefinition<Type>
             Class<? extends Throwable> exceptionType3) {
         if (this.getRouteConfiguration() != null) {
             // this is part of route configuration
-            return this.getRouteConfiguration().onException(exceptionType1, 
exceptionType2, exceptionType2);
+            return this.getRouteConfiguration().onException(exceptionType1, 
exceptionType2, exceptionType3);
         }
         OnExceptionDefinition answer = new OnExceptionDefinition(
                 Arrays.asList(exceptionType1, exceptionType2, exceptionType3));
@@ -2598,11 +2603,7 @@ public abstract class ProcessorDefinition<Type extends 
ProcessorDefinition<Type>
      */
     public Type bean(Object bean) {
         BeanDefinition answer = new BeanDefinition();
-        if (bean instanceof String str) {
-            answer.setRef(str);
-        } else {
-            answer.setBean(bean);
-        }
+        setBeanOrRef(answer, bean);
         addOutput(answer);
         return asType();
     }
@@ -2614,6 +2615,19 @@ public abstract class ProcessorDefinition<Type extends 
ProcessorDefinition<Type>
      * @param  bean the bean to invoke, or a reference to a bean if the type 
is a String
      * @return      the builder
      */
+    private static void setBeanOrRef(BeanDefinition answer, Object bean) {
+        if (bean instanceof String str) {
+            // type:com.foo.MyBean refers to the class of the bean
+            if (str.startsWith("type:")) {
+                answer.setBeanType(str.substring(5));
+            } else {
+                answer.setRef(str);
+            }
+        } else {
+            answer.setBean(bean);
+        }
+    }
+
     public Type bean(Supplier<Object> bean) {
         return bean(bean.get());
     }
@@ -2629,15 +2643,7 @@ public abstract class ProcessorDefinition<Type extends 
ProcessorDefinition<Type>
      */
     public Type bean(Object bean, String method) {
         BeanDefinition answer = new BeanDefinition();
-        if (bean instanceof String str) {
-            if (str.startsWith("type:")) {
-                answer.setBeanType(str.substring(5));
-            } else {
-                answer.setRef(str);
-            }
-        } else {
-            answer.setBean(bean);
-        }
+        setBeanOrRef(answer, bean);
         answer.setMethod(method);
         addOutput(answer);
         return asType();
@@ -2678,11 +2684,7 @@ public abstract class ProcessorDefinition<Type extends 
ProcessorDefinition<Type>
      */
     public Type bean(Object bean, BeanScope scope) {
         BeanDefinition answer = new BeanDefinition();
-        if (bean instanceof String str) {
-            answer.setRef(str);
-        } else {
-            answer.setBean(bean);
-        }
+        setBeanOrRef(answer, bean);
         answer.setScope(scope);
         addOutput(answer);
         return asType();
@@ -2699,11 +2701,7 @@ public abstract class ProcessorDefinition<Type extends 
ProcessorDefinition<Type>
      */
     public Type bean(Object bean, String method, BeanScope scope) {
         BeanDefinition answer = new BeanDefinition();
-        if (bean instanceof String str) {
-            answer.setRef(str);
-        } else {
-            answer.setBean(bean);
-        }
+        setBeanOrRef(answer, bean);
         answer.setMethod(method);
         answer.setScope(scope);
         addOutput(answer);
@@ -3506,7 +3504,8 @@ public abstract class ProcessorDefinition<Type extends 
ProcessorDefinition<Type>
      * @see                org.apache.camel.processor.Enricher
      */
     public EnrichClause<ProcessorDefinition<Type>> enrichWith(@AsEndpointUri 
EndpointProducerBuilder resourceUri) {
-        return enrichWith(resourceUri.getRawUri());
+        // same as the other endpoint builder variants (the uri may be dynamic)
+        return enrichWith(resourceUri, false, false);
     }
 
     /**
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/model/ProcessorDefinitionBuilderEdgeCasesTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/model/ProcessorDefinitionBuilderEdgeCasesTest.java
new file mode 100644
index 000000000000..1386706cd623
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/model/ProcessorDefinitionBuilderEdgeCasesTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.model;
+
+import java.io.IOException;
+import java.lang.reflect.Proxy;
+import java.util.List;
+
+import org.apache.camel.BeanScope;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.EndpointProducerBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.builder.RouteConfigurationBuilder;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProcessorDefinitionBuilderEdgeCasesTest extends 
ContextTestSupport {
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    private RouteDefinition route(RouteBuilder builder) throws Exception {
+        context.addRoutes(builder);
+        context.start();
+        return 
context.getRouteDefinitions().get(context.getRouteDefinitions().size() - 1);
+    }
+
+    @Test
+    public void testNoteOnChoice() throws Exception {
+        RouteDefinition route = route(new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start").choice().note("my 
note").when(header("a")).to("mock:a").end();
+            }
+        });
+        assertThat(route.getOutputs().get(0).getNote()).isEqualTo("my note");
+    }
+
+    @Test
+    public void testThrottleWithCorrelationKey() throws Exception {
+        RouteDefinition route = route(new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start").throttle(header("max"), 7L).to("mock:a");
+            }
+        });
+        ThrottleDefinition throttle = (ThrottleDefinition) 
route.getOutputs().get(0);
+        assertThat(throttle.getExpression().getExpression()).isEqualTo("max");
+        
assertThat(throttle.getCorrelationExpression().getExpressionType().getExpression()).isEqualTo("7");
+    }
+
+    @Test
+    public void testRouteConfigurationOnExceptionWithThreeExceptions() throws 
Exception {
+        context.addRoutes(new RouteConfigurationBuilder() {
+            @Override
+            public void configuration() {
+                
routeConfiguration().onException(IOException.class).handled(true).to("mock:io")
+                        .onException(IllegalArgumentException.class, 
IllegalStateException.class,
+                                UnsupportedOperationException.class)
+                        .handled(true).to("mock:other");
+            }
+        });
+        List<String> exceptions = 
context.getRouteConfigurationDefinitions().get(0).getOnExceptions().get(1).getExceptions();
+        
assertThat(exceptions).containsExactly(IllegalArgumentException.class.getName(),
+                IllegalStateException.class.getName(), 
UnsupportedOperationException.class.getName());
+    }
+
+    @Test
+    public void testSimpleWithResultTypePrettyAndTrim() throws Exception {
+        route(new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start").setBody().simple("${body}", String.class, 
false, true).to("mock:result");
+            }
+        });
+        getMockEndpoint("mock:result").expectedBodiesReceived("x");
+        template.sendBody("direct:start", "  x  ");
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testEndDoTryFromBlockInsideCatchAndFinally() throws Exception {
+        RouteDefinition route = route(new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start")
+                        .doTry()
+                            .to("mock:try")
+                        .doCatch(Exception.class)
+                            .filter(header("x"))
+                                .to("mock:filtered")
+                            .endDoTry()
+                        .doFinally()
+                            .filter(header("y"))
+                                .to("mock:finally")
+                            .endDoTry()
+                        .end()
+                        .to("mock:result");
+            }
+        });
+        TryDefinition doTry = (TryDefinition) route.getOutputs().get(0);
+        assertThat(doTry.getCatchClauses()).hasSize(1);
+        assertThat(doTry.getFinallyClause()).isNotNull();
+        assertThat(route.getOutputs().get(1)).isInstanceOf(ToDefinition.class);
+    }
+
+    @Test
+    public void testEnrichWithEndpointBuilderIsDynamic() throws Exception {
+        RouteDefinition route = route(new RouteBuilder() {
+            @Override
+            public void configure() {
+                
from("direct:start").enrichWith(producerBuilder("direct:${header.target}")).body((a,
 b) -> b).to("mock:result");
+            }
+        });
+        EnrichDefinition enrich = (EnrichDefinition) route.getOutputs().get(0);
+        assertThat(enrich.getExpression().getLanguage()).isEqualTo("simple");
+    }
+
+    @Test
+    public void testBeanTypePrefix() throws Exception {
+        RouteDefinition route = route(new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start")
+                        .bean("type:" + MyBean.class.getName())
+                        .bean("type:" + MyBean.class.getName(), 
BeanScope.Prototype)
+                        .bean("type:" + MyBean.class.getName(), "hello", 
BeanScope.Prototype)
+                        .to("mock:result");
+            }
+        });
+        for (int i = 0; i < 3; i++) {
+            BeanDefinition bean = (BeanDefinition) route.getOutputs().get(i);
+            assertThat(bean.getBeanType()).isEqualTo(MyBean.class.getName());
+            assertThat(bean.getRef()).isNull();
+        }
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("Hello Hello 
Hello World");
+        template.sendBody("direct:start", "World");
+        assertMockEndpointsSatisfied();
+    }
+
+    private static EndpointProducerBuilder producerBuilder(String uri) {
+        return (EndpointProducerBuilder) 
Proxy.newProxyInstance(EndpointProducerBuilder.class.getClassLoader(),
+                new Class<?>[] { EndpointProducerBuilder.class }, (proxy, 
method, args) -> {
+                    if ("getRawUri".equals(method.getName()) || 
"getUri".equals(method.getName())) {
+                        return uri;
+                    }
+                    throw new UnsupportedOperationException(method.getName());
+                });
+    }
+
+    public static class MyBean {
+        public String hello(String body) {
+            return "Hello " + body;
+        }
+    }
+}

Reply via email to