You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2021/06/25 07:22:33 UTC
[camel] branch main updated: CAMEL-16759: camel-core - Kamelet add
support for factory method in #class local bean
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 fa51da1 CAMEL-16759: camel-core - Kamelet add support for factory method in #class local bean
fa51da1 is described below
commit fa51da1029effa00b378293154a93d297c6c88af
Author: Claus Ibsen <cl...@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 19fe59e..e72531e 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
@@ -1207,7 +1207,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);
@@ -1281,7 +1291,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, ',');