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

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

commit 37a874791122091c8bc0c35373ed222064994365
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Fri Jun 25 09:21:24 2021 +0200

    CAMEL-16759: camel-core - Kamelet add support for factory method in #class 
local bean
---
 .../java/org/apache/camel/impl/DefaultModel.java   | 69 ++++++++++++++++++---
 .../camel/builder/MyConstructorProcessor.java      | 37 ++++++++++++
 .../camel/builder/RouteTemplateLocalBeanTest.java  | 70 ++++++++++++++++++++++
 .../camel/support/PropertyBindingSupport.java      | 24 +++++++-
 4 files changed, 189 insertions(+), 11 deletions(-)

diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
index 453a205..5802e61 100644
--- 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
+++ 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
@@ -70,6 +70,7 @@ import org.apache.camel.support.ScriptHelper;
 import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.util.AntPathMatcher;
 import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.function.Suppliers;
 
 public class DefaultModel implements Model {
@@ -425,19 +426,69 @@ public class DefaultModel implements Model {
                     }));
                 }
             } else if (b.getBeanClass() != null || b.getType() != null && 
b.getType().startsWith("#class:")) {
-                Class<?> clazz = b.getBeanClass() != null
-                        ? b.getBeanClass() : 
camelContext.getClassResolver().resolveMandatoryClass(b.getType().substring(7));
-                // we only have the bean class so we use that to create a new 
bean via the injector
-                // and memorize so the bean is only created once and the local 
bean is the same
-                // if a route template refers to the local bean multiple times
-                routeTemplateContext.bind(b.getName(), clazz,
-                        Suppliers.memorize(() -> {
-                            Object local = 
camelContext.getInjector().newInstance(clazz);
+                // if there is a factory method then the class/bean should be 
created in a different way
+                String className = null;
+                String factoryMethod = null;
+                String parameters = null;
+                if (b.getType() != null) {
+                    className = b.getType().substring(7);
+                    if (className.endsWith(")") && className.indexOf('(') != 
-1) {
+                        parameters = StringHelper.after(className, "(");
+                        parameters = parameters.substring(0, 
parameters.length() - 1); // clip last )
+                        className = StringHelper.before(className, "(");
+                    }
+                    if (className != null && className.indexOf('#') != -1) {
+                        factoryMethod = StringHelper.after(className, "#");
+                        className = StringHelper.before(className, "#");
+                    }
+                }
+                if (className != null && (factoryMethod != null || parameters 
!= null)) {
+                    final Class<?> clazz = 
camelContext.getClassResolver().resolveMandatoryClass(className);
+                    final String fqn = className;
+                    final String fm = factoryMethod;
+                    final String fp = parameters;
+                    routeTemplateContext.bind(b.getName(), Object.class, 
Suppliers.memorize(() -> {
+                        try {
+                            Object local;
+                            if (fm != null) {
+                                if (fp != null) {
+                                    // special to support factory method 
parameters
+                                    local = 
PropertyBindingSupport.newInstanceFactoryParameters(camelContext, clazz, fm, 
fp);
+                                } else {
+                                    local = 
camelContext.getInjector().newInstance(clazz, fm);
+                                }
+                                if (local == null) {
+                                    throw new IllegalStateException(
+                                            "Cannot create bean instance using 
factory method: " + fqn + "#" + fm);
+                                }
+                            } else {
+                                // special to support constructor parameters
+                                local = 
PropertyBindingSupport.newInstanceConstructorParameters(camelContext, clazz, 
fp);
+                            }
                             if (!props.isEmpty()) {
                                 setPropertiesOnTarget(camelContext, local, 
props);
                             }
                             return local;
-                        }));
+                        } catch (Exception e) {
+                            throw new IllegalStateException(
+                                    "Cannot create bean: " + b.getType());
+                        }
+                    }));
+                } else {
+                    Class<?> clazz = b.getBeanClass() != null
+                            ? b.getBeanClass() : 
camelContext.getClassResolver().resolveMandatoryClass(className);
+                    // we only have the bean class so we use that to create a 
new bean via the injector
+                    // and memorize so the bean is only created once and the 
local bean is the same
+                    // if a route template refers to the local bean multiple 
times
+                    routeTemplateContext.bind(b.getName(), clazz,
+                            Suppliers.memorize(() -> {
+                                Object local = 
camelContext.getInjector().newInstance(clazz);
+                                if (!props.isEmpty()) {
+                                    setPropertiesOnTarget(camelContext, local, 
props);
+                                }
+                                return local;
+                            }));
+                }
             } else if (b.getType() != null && 
b.getType().startsWith("#type:")) {
                 Class<?> clazz = 
camelContext.getClassResolver().resolveMandatoryClass(b.getType().substring(6));
                 Set<?> found = 
getCamelContext().getRegistry().findByType(clazz);
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/builder/MyConstructorProcessor.java
 
b/core/camel-core/src/test/java/org/apache/camel/builder/MyConstructorProcessor.java
new file mode 100644
index 0000000..ae77098
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/builder/MyConstructorProcessor.java
@@ -0,0 +1,37 @@
+/*
+ * 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.builder;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+
+public class MyConstructorProcessor implements Processor {
+
+    private String prefix = "";
+
+    public MyConstructorProcessor() {
+    }
+
+    public MyConstructorProcessor(String prefix) {
+        this.prefix = prefix;
+    }
+
+    @Override
+    public void process(Exchange exchange) throws Exception {
+        exchange.getMessage().setBody(prefix + 
exchange.getMessage().getBody());
+    }
+}
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateLocalBeanTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateLocalBeanTest.java
index 7228b50..3780e38 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateLocalBeanTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateLocalBeanTest.java
@@ -692,6 +692,70 @@ public class RouteTemplateLocalBeanTest extends 
ContextTestSupport {
         context.stop();
     }
 
+    @Test
+    public void testLocalBeanFactoryMethod() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                
routeTemplate("myTemplate").templateParameter("foo").templateParameter("bar")
+                        .templateBean("myBar")
+                        
.type("#class:org.apache.camel.builder.RouteTemplateLocalBeanTest#createBuilderProcessorThree('MyPrefix
 ')")
+                        .end()
+                        .from("direct:{{foo}}")
+                        .to("bean:{{bar}}");
+            }
+        });
+
+        context.start();
+
+        TemplatedRouteBuilder.builder(context, "myTemplate")
+                .parameter("foo", "one")
+                .parameter("bar", "myBar")
+                .routeId("myRoute")
+                .add();
+
+        assertEquals(1, context.getRoutes().size());
+
+        Object out = template.requestBody("direct:one", "World");
+        assertEquals("MyPrefix Builder3 World", out);
+
+        // should not be a global bean
+        assertNull(context.getRegistry().lookupByName("myBar"));
+
+        context.stop();
+    }
+
+    @Test
+    public void testLocalBeanConstructorParameter() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                
routeTemplate("myTemplate").templateParameter("foo").templateParameter("bar")
+                        
.templateBean("myBar").type("#class:org.apache.camel.builder.MyConstructorProcessor('MyCtr
 ')").end()
+                        .from("direct:{{foo}}")
+                        .to("bean:{{bar}}");
+            }
+        });
+
+        context.start();
+
+        TemplatedRouteBuilder.builder(context, "myTemplate")
+                .parameter("foo", "one")
+                .parameter("bar", "myBar")
+                .routeId("myRoute")
+                .add();
+
+        assertEquals(1, context.getRoutes().size());
+
+        Object out = template.requestBody("direct:one", "World");
+        assertEquals("MyCtr World", out);
+
+        // should not be a global bean
+        assertNull(context.getRegistry().lookupByName("myBar"));
+
+        context.stop();
+    }
+
     public static class BuilderTwoProcessor implements Processor {
 
         private String prefix = "";
@@ -739,4 +803,10 @@ public class RouteTemplateLocalBeanTest extends 
ContextTestSupport {
         return new BuilderTwoProcessor(rtc.getProperty("greeting", 
String.class));
     }
 
+    public static Processor createBuilderProcessorThree(String prefix) {
+        BuilderThreeProcessor answer = new BuilderThreeProcessor();
+        answer.setPrefix(prefix);
+        return answer;
+    }
+
 }
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
index aeee7e3..9d03a52 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
@@ -1202,7 +1202,17 @@ public final class PropertyBindingSupport {
         return true;
     }
 
-    private static Object newInstanceConstructorParameters(CamelContext 
camelContext, Class<?> type, String parameters)
+    /**
+     * Creates a new bean instance using the constructor that takes the given 
set of parameters.
+     *
+     * @param  camelContext the camel context
+     * @param  type         the class type of the bean to create
+     * @param  parameters   the parameters for the constructor
+     * @return              the created bean, or null if there was no 
constructor that matched the given set of
+     *                      parameters
+     * @throws Exception    is thrown if error creating the bean
+     */
+    public static Object newInstanceConstructorParameters(CamelContext 
camelContext, Class<?> type, String parameters)
             throws Exception {
         String[] params = StringQuoteHelper.splitSafeQuote(parameters, ',');
         Constructor found = findMatchingConstructor(type.getConstructors(), 
params);
@@ -1276,7 +1286,17 @@ public final class PropertyBindingSupport {
         return candidates.size() == 1 ? candidates.get(0) : fallbackCandidate;
     }
 
-    private static Object newInstanceFactoryParameters(
+    /**
+     * Creates a new bean instance using a public static factory method from 
the given class
+     *
+     * @param  camelContext the camel context
+     * @param  type         the class with the public static factory method
+     * @param  parameters   optional parameters for the factory method
+     * @return              the created bean, or null if there was no factory 
method (optionally matched the given set
+     *                      of parameters)
+     * @throws Exception    is thrown if error creating the bean
+     */
+    public static Object newInstanceFactoryParameters(
             CamelContext camelContext, Class<?> type, String factoryMethod, 
String parameters)
             throws Exception {
         String[] params = StringQuoteHelper.splitSafeQuote(parameters, ',');

Reply via email to