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 f18c1e017d82 CAMEL-22894: Extract bean: Simple function into 
BeanFunctionFactory
f18c1e017d82 is described below

commit f18c1e017d82398cc7cdd60558494e73b51c578f
Author: Adriano Machado <[email protected]>
AuthorDate: Wed May 27 14:33:38 2026 -0400

    CAMEL-22894: Extract bean: Simple function into BeanFunctionFactory
    
    Extract the bean: function from SimpleFunctionExpression into a new 
BeanFunctionFactory
    with a shared parseBeanRemainder() helper, eliminating duplicated 
ref/method/scope parsing
    between the expression and code-generation paths. Also fixes createCode to 
handle the
    scope-only case (bean:ref?scope=Singleton) that was previously silently 
ignored.
    
    Closes #23569
---
 .../language/simple/SimpleFunctionDispatcher.java  |   4 +-
 .../simple/ast/SimpleFunctionExpression.java       | 103 ----------------
 .../simple/functions/BeanFunctionFactory.java      | 129 +++++++++++++++++++++
 .../simple/functions/BeanFunctionFactoryTest.java  | 115 ++++++++++++++++++
 4 files changed, 247 insertions(+), 104 deletions(-)

diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java
index f4c9af9cbbd4..b432af0e94d4 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionDispatcher.java
@@ -21,6 +21,7 @@ import java.util.function.Predicate;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
+import org.apache.camel.language.simple.functions.BeanFunctionFactory;
 import org.apache.camel.language.simple.functions.BodyFunctionFactory;
 import org.apache.camel.language.simple.functions.CollateFunctionFactory;
 import org.apache.camel.language.simple.functions.CollectionFunctionFactory;
@@ -79,7 +80,8 @@ public final class SimpleFunctionDispatcher {
             new TypeFunctionFactory(),
             new DateFunctionFactory(),
             new MessageFunctionFactory(),
-            new OutputFunctionFactory());
+            new OutputFunctionFactory(),
+            new BeanFunctionFactory());
 
     private static final List<Entry> EXPRESSION_ENTRIES = List.of(
             new Entry("camel-attachments", 
SimpleFunctionDispatcher::isAttachmentFunction),
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
index ec5b0ea65697..43fab4c55d7b 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
@@ -16,12 +16,10 @@
  */
 package org.apache.camel.language.simple.ast;
 
-import java.net.URISyntaxException;
 import java.util.Map;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
-import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.language.simple.BaseSimpleParser;
 import org.apache.camel.language.simple.SimpleExpressionBuilder;
 import org.apache.camel.language.simple.SimpleFunctionDispatcher;
@@ -30,14 +28,12 @@ import 
org.apache.camel.language.simple.SimplePredicateParser;
 import org.apache.camel.language.simple.functions.DirectFunctionFactory;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.language.simple.types.SimpleToken;
-import org.apache.camel.spi.Language;
 import org.apache.camel.support.PluginHelper;
 import org.apache.camel.support.builder.ExpressionBuilder;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.OgnlHelper;
 import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.StringQuoteHelper;
-import org.apache.camel.util.URISupport;
 
 /**
  * Represents one of built-in functions of the <a 
href="http://camel.apache.org/simple.html";>simple language</a>
@@ -190,61 +186,6 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             }
         }
 
-        // bean: prefix
-        remainder = ifStartsWithReturnRemainder("bean:", function);
-        if (remainder != null) {
-            Language bean = camelContext.resolveLanguage("bean");
-            String ref = remainder;
-            Object method = null;
-            Object scope = null;
-
-            // we support different syntax for bean function
-            if (remainder.contains("?method=") || 
remainder.contains("?scope=")) {
-                ref = StringHelper.before(remainder, "?");
-                String query = StringHelper.after(remainder, "?");
-                try {
-                    Map<String, Object> map = URISupport.parseQuery(query);
-                    method = map.get("method");
-                    scope = map.get("scope");
-                } catch (URISyntaxException e) {
-                    throw RuntimeCamelException.wrapRuntimeException(e);
-                }
-            } else {
-                //first check case :: because of my.own.Bean::method
-                int doubleColonIndex = remainder.indexOf("::");
-                //need to check that not inside params
-                int beginOfParameterDeclaration = remainder.indexOf('(');
-                if (doubleColonIndex > 0 && (!remainder.contains("(") || 
doubleColonIndex < beginOfParameterDeclaration)) {
-                    ref = remainder.substring(0, doubleColonIndex);
-                    method = remainder.substring(doubleColonIndex + 2);
-                } else {
-                    int idx = remainder.indexOf('.');
-                    if (idx > 0) {
-                        ref = remainder.substring(0, idx);
-                        method = remainder.substring(idx + 1);
-                    }
-                }
-            }
-
-            Class<?> type = null;
-            if (ref != null && ref.startsWith("type:")) {
-                try {
-                    type = 
camelContext.getClassResolver().resolveMandatoryClass(ref.substring(5));
-                    ref = null;
-                } catch (ClassNotFoundException e) {
-                    throw RuntimeCamelException.wrapRuntimeException(e);
-                }
-            }
-
-            // there are parameters then map them into properties
-            Object[] properties = new Object[7];
-            properties[3] = type;
-            properties[4] = ref;
-            properties[2] = method;
-            properties[5] = scope;
-            return bean.createExpression(null, properties);
-        }
-
         // miscellaneous and other built-in functions
         Expression builtIn = 
SimpleFunctionDispatcher.tryCreateBuiltIn(camelContext, function, 
token.getIndex());
         if (builtIn != null) {
@@ -422,50 +363,6 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             return createCodeFileExpression(remainder);
         }
 
-        // bean: prefix
-        remainder = ifStartsWithReturnRemainder("bean:", function);
-        if (remainder != null) {
-            String ref = remainder;
-            Object method = null;
-            Object scope = null;
-
-            // we support different syntax for bean function
-            if (remainder.contains("?method=") || 
remainder.contains("?scope=")) {
-                ref = StringHelper.before(remainder, "?");
-                String query = StringHelper.after(remainder, "?");
-                try {
-                    Map<String, Object> map = URISupport.parseQuery(query);
-                    method = map.get("method");
-                    scope = map.get("scope");
-                } catch (URISyntaxException e) {
-                    throw RuntimeCamelException.wrapRuntimeException(e);
-                }
-            } else {
-                //first check case :: because of my.own.Bean::method
-                int doubleColonIndex = remainder.indexOf("::");
-                //need to check that not inside params
-                int beginOfParameterDeclaration = remainder.indexOf('(');
-                if (doubleColonIndex > 0 && (!remainder.contains("(") || 
doubleColonIndex < beginOfParameterDeclaration)) {
-                    ref = remainder.substring(0, doubleColonIndex);
-                    method = remainder.substring(doubleColonIndex + 2);
-                } else {
-                    int idx = remainder.indexOf('.');
-                    if (idx > 0) {
-                        ref = remainder.substring(0, idx);
-                        method = remainder.substring(idx + 1);
-                    }
-                }
-            }
-            ref = ref.trim();
-            if (method != null && scope != null) {
-                return "bean(exchange, bean, \"" + ref + "\", \"" + method + 
"\", \"" + scope + "\")";
-            } else if (method != null) {
-                return "bean(exchange, bean, \"" + ref + "\", \"" + method + 
"\", null)";
-            } else {
-                return "bean(exchange, bean, \"" + ref + "\", null, null)";
-            }
-        }
-
         // miscellaneous and other built-in functions
         String builtIn = 
SimpleFunctionDispatcher.tryCreateCodeBuiltIn(camelContext, function, 
token.getIndex());
         if (builtIn != null) {
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/BeanFunctionFactory.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/BeanFunctionFactory.java
new file mode 100644
index 000000000000..c4839f7f1b9e
--- /dev/null
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/functions/BeanFunctionFactory.java
@@ -0,0 +1,129 @@
+/*
+ * 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.language.simple.functions;
+
+import java.net.URISyntaxException;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Expression;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.spi.Language;
+import org.apache.camel.spi.SimpleLanguageFunctionFactory;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.URISupport;
+
+import static 
org.apache.camel.language.simple.SimpleFunctionHelper.ifStartsWithReturnRemainder;
+
+/**
+ * Built-in Simple function for bean invocation: {@code ${bean:ref}}, {@code 
${bean:ref.method}},
+ * {@code ${bean:ref::method}}, {@code ${bean:ref?method=m&scope=s}}, {@code 
${bean:type:FQN}}.
+ */
+public final class BeanFunctionFactory implements 
SimpleLanguageFunctionFactory {
+
+    @Override
+    public Expression createFunction(CamelContext camelContext, String 
function, int index) {
+        String remainder = ifStartsWithReturnRemainder("bean:", function);
+        if (remainder == null) {
+            return null;
+        }
+
+        String[] parsed = parseBeanRemainder(remainder);
+        String ref = parsed[0];
+        String method = parsed[1];
+        String scope = parsed[2];
+
+        Class<?> type = null;
+        if (ref != null && ref.startsWith("type:")) {
+            try {
+                type = 
camelContext.getClassResolver().resolveMandatoryClass(ref.substring(5));
+                ref = null;
+            } catch (ClassNotFoundException e) {
+                throw RuntimeCamelException.wrapRuntimeException(e);
+            }
+        }
+
+        Language bean = camelContext.resolveLanguage("bean");
+        Object[] properties = new Object[7];
+        properties[2] = method;
+        properties[3] = type;
+        properties[4] = ref;
+        properties[5] = scope;
+        return bean.createExpression(null, properties);
+    }
+
+    @Override
+    @SuppressWarnings("deprecation")
+    public String createCode(CamelContext camelContext, String function, int 
index) {
+        String remainder = ifStartsWithReturnRemainder("bean:", function);
+        if (remainder == null) {
+            return null;
+        }
+
+        String[] parsed = parseBeanRemainder(remainder);
+        String ref = parsed[0];
+        String method = parsed[1];
+        String scope = parsed[2];
+
+        if (method != null && scope != null) {
+            return "bean(exchange, bean, \"" + ref + "\", \"" + method + "\", 
\"" + scope + "\")";
+        } else if (method != null) {
+            return "bean(exchange, bean, \"" + ref + "\", \"" + method + "\", 
null)";
+        } else if (scope != null) {
+            return "bean(exchange, bean, \"" + ref + "\", null, \"" + scope + 
"\")";
+        } else {
+            return "bean(exchange, bean, \"" + ref + "\", null, null)";
+        }
+    }
+
+    private static String[] parseBeanRemainder(String remainder) {
+        String ref = remainder;
+        String method = null;
+        String scope = null;
+
+        if (remainder.contains("?method=") || remainder.contains("?scope=")) {
+            ref = StringHelper.before(remainder, "?");
+            String query = StringHelper.after(remainder, "?");
+            try {
+                Map<String, Object> map = URISupport.parseQuery(query);
+                Object m = map.get("method");
+                Object s = map.get("scope");
+                method = m != null ? m.toString() : null;
+                scope = s != null ? s.toString() : null;
+            } catch (URISyntaxException e) {
+                throw RuntimeCamelException.wrapRuntimeException(e);
+            }
+        } else {
+            // first check :: because of my.own.Bean::method
+            int doubleColonIndex = remainder.indexOf("::");
+            // need to check that not inside params
+            int beginOfParameterDeclaration = remainder.indexOf('(');
+            if (doubleColonIndex > 0 && (!remainder.contains("(") || 
doubleColonIndex < beginOfParameterDeclaration)) {
+                ref = remainder.substring(0, doubleColonIndex);
+                method = remainder.substring(doubleColonIndex + 2);
+            } else {
+                int idx = remainder.indexOf('.');
+                if (idx > 0) {
+                    ref = remainder.substring(0, idx);
+                    method = remainder.substring(idx + 1);
+                }
+            }
+        }
+
+        return new String[] { ref.trim(), method, scope };
+    }
+}
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/BeanFunctionFactoryTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/BeanFunctionFactoryTest.java
new file mode 100644
index 000000000000..3378cd188ecd
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/functions/BeanFunctionFactoryTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.language.simple.functions;
+
+import org.apache.camel.spi.SimpleLanguageFunctionFactory;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class BeanFunctionFactoryTest extends 
AbstractSimpleFunctionFactoryTestSupport {
+
+    public static final class MyBean {
+        public String hello(String name) {
+            return "Hello " + name;
+        }
+
+        public String greet() {
+            return "Greetings";
+        }
+    }
+
+    @BeforeEach
+    public void registerBean() {
+        context.getRegistry().bind("myBean", new MyBean());
+    }
+
+    @Override
+    protected SimpleLanguageFunctionFactory createFactory() {
+        return new BeanFunctionFactory();
+    }
+
+    // --- dot notation ---
+
+    @Test
+    public void testBeanDotMethod() {
+        exchange.getIn().setBody("World");
+        assertEquals("Hello World", evaluate("bean:myBean.hello", 
String.class));
+    }
+
+    @Test
+    public void testBeanNoMethod() {
+        assertEquals("Greetings", evaluate("bean:myBean.greet", String.class));
+    }
+
+    // --- double-colon notation ---
+
+    @Test
+    public void testBeanDoubleColonMethod() {
+        exchange.getIn().setBody("World");
+        assertEquals("Hello World", evaluate("bean:myBean::hello", 
String.class));
+    }
+
+    // --- query-string notation ---
+
+    @Test
+    public void testBeanQueryMethod() {
+        exchange.getIn().setBody("World");
+        assertEquals("Hello World", evaluate("bean:myBean?method=hello", 
String.class));
+    }
+
+    // --- createCode ---
+
+    @Test
+    public void testCreateCodeRefOnly() {
+        assertEquals("bean(exchange, bean, \"myBean\", null, null)", 
createCode("bean:myBean"));
+    }
+
+    @Test
+    public void testCreateCodeDotMethod() {
+        assertEquals("bean(exchange, bean, \"myBean\", \"hello\", null)", 
createCode("bean:myBean.hello"));
+    }
+
+    @Test
+    public void testCreateCodeDoubleColon() {
+        assertEquals("bean(exchange, bean, \"myBean\", \"hello\", null)", 
createCode("bean:myBean::hello"));
+    }
+
+    @Test
+    public void testCreateCodeQueryMethodAndScope() {
+        assertEquals(
+                "bean(exchange, bean, \"myBean\", \"hello\", \"Singleton\")",
+                createCode("bean:myBean?method=hello&scope=Singleton"));
+    }
+
+    @Test
+    public void testCreateCodeScopeOnly() {
+        assertEquals(
+                "bean(exchange, bean, \"myBean\", null, \"Singleton\")",
+                createCode("bean:myBean?scope=Singleton"));
+    }
+
+    // --- unrecognized ---
+
+    @Test
+    public void testUnrecognizedFunction() {
+        assertNull(createFactory().createFunction(context, "unknown", 0));
+        assertNull(createFactory().createCode(context, "unknown", 0));
+    }
+}

Reply via email to