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));
+ }
+}