This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fac-bean in repository https://gitbox.apache.org/repos/asf/camel.git
commit 0c918e71c86cddaa1e4cd3e0abe5a83d8ec5f72d Author: Claus Ibsen <[email protected]> AuthorDate: Tue Sep 26 12:56:12 2023 +0200 CAMEL-19912: camel-core-model - Add factory-bean parameter --- .../main/java/org/apache/camel/spi/Injector.java | 12 +++++ .../apache/camel/impl/engine/DefaultInjector.java | 13 ++++-- .../java/org/apache/camel/impl/DefaultModel.java | 21 ++++++++- .../camel/model/app/RegistryBeanDefinition.java | 10 ++++ .../xml/AbstractCamelContextFactoryBeanTest.java | 5 ++ .../camel/support/PropertyBindingSupportTest.java | 5 ++ .../org/apache/camel/util/ReflectionInjector.java | 26 ++++++++++- ...opertyBindingSupportClassFactoryMethodTest.java | 15 +++--- ...indingSupportHelperClassFactoryMethodTest.java} | 54 ++++++++++++++++++---- .../org/apache/camel/support/EndpointHelper.java | 20 +++++++- .../camel/support/PropertyBindingSupport.java | 42 ++++++++++++++--- .../java/org/apache/camel/xml/in/ModelParser.java | 1 + .../java/org/apache/camel/xml/out/ModelWriter.java | 1 + .../org/apache/camel/xml/LwModelToXMLDumper.java | 12 +++-- .../camel/xml/jaxb/JaxbModelToXMLDumper.java | 11 +++-- .../org/apache/camel/yaml/out/ModelWriter.java | 1 + .../org/apache/camel/yaml/LwModelToYAMLDumper.java | 8 ++-- .../modules/ROOT/pages/property-binding.adoc | 3 ++ .../camel/main/download/KameletMainInjector.java | 9 ++++ .../xml/blueprint/BlueprintXmlBeansHandler.java | 12 +++-- .../main/xml/spring/SpringXmlBeansHandler.java | 5 +- .../src/main/docs/java-xml-io-dsl.adoc | 34 ++++++++++++++ .../camel/dsl/xml/io/XmlRoutesBuilderLoader.java | 6 ++- .../apache/camel/dsl/xml/io/XmlLoadAppTest.java | 25 +++++++++- .../apache/camel/dsl/xml/io/beans/MyFacBean.java | 2 +- .../io/beans/{MyFacBean.java => MyFacHelper.java} | 31 +------------ .../org/apache/camel/dsl/xml/io/camel-app7.xml | 41 ++++++++++++++++ .../dsl/yaml/deserializers/ModelDeserializers.java | 6 +++ .../dsl/yaml/deserializers/BeansDeserializer.java | 20 ++++---- .../generated/resources/schema/camelYamlDsl.json | 3 ++ .../camel-yaml-dsl/src/main/docs/yaml-dsl.adoc | 35 ++++++++++++++ .../org/apache/camel/dsl/yaml/BeansTest.groovy | 24 ++++++++++ .../dsl/yaml/support/model/MyFacHelper.groovy} | 35 ++------------ 33 files changed, 427 insertions(+), 121 deletions(-) diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/Injector.java b/core/camel-api/src/main/java/org/apache/camel/spi/Injector.java index c59bc50f32d..69df0a6959b 100644 --- a/core/camel-api/src/main/java/org/apache/camel/spi/Injector.java +++ b/core/camel-api/src/main/java/org/apache/camel/spi/Injector.java @@ -42,6 +42,18 @@ public interface Injector { */ <T> T newInstance(Class<T> type, String factoryMethod); + /** + * Instantiates a new instance of the given type by using the factory class (this will not perform bean post + * processing) + * + * @param type the type of object to create + * @param factoryClass to create the new instance via factory class + * @param factoryMethod to create the new instance via factory method which must be public static and return the + * type + * @return a newly created instance + */ + <T> T newInstance(Class<T> type, Class<?> factoryClass, String factoryMethod); + /** * Instantiates a new instance of the given type; possibly injecting values into the object in the process (bean * post processing if enabled) diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultInjector.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultInjector.java index 2b4b47cdf01..332dd05b64f 100644 --- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultInjector.java +++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultInjector.java @@ -49,18 +49,25 @@ public class DefaultInjector implements Injector { @Override public <T> T newInstance(Class<T> type, String factoryMethod) { + return newInstance(type, null, factoryMethod); + } + + @Override + public <T> T newInstance(Class<T> type, Class<?> factoryClass, String factoryMethod) { + Class<?> target = factoryClass != null ? factoryClass : type; T answer = null; try { // lookup factory method - Method fm = type.getMethod(factoryMethod); - if (Modifier.isStatic(fm.getModifiers()) && Modifier.isPublic(fm.getModifiers()) && fm.getReturnType() == type) { + Method fm = target.getMethod(factoryMethod); + if (Modifier.isStatic(fm.getModifiers()) && Modifier.isPublic(fm.getModifiers()) + && fm.getReturnType() != Void.class) { Object obj = fm.invoke(null); answer = type.cast(obj); } // inject camel context if needed CamelContextAware.trySetCamelContext(answer, camelContext); } catch (Exception e) { - throw new RuntimeCamelException("Error invoking factory method: " + factoryMethod + " on class: " + type, e); + throw new RuntimeCamelException("Error invoking factory method: " + factoryMethod + " on class: " + target, e); } return answer; } 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 729bb3ce5ff..1f651f3fc24 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 @@ -631,6 +631,22 @@ public class DefaultModel implements Model { if (className != null && (factoryMethod != null || parameters != null)) { final CamelContext camelContext = routeTemplateContext.getCamelContext(); final Class<?> clazz = camelContext.getClassResolver().resolveMandatoryClass(className); + Class<?> fc = null; + if (factoryMethod != null) { + String typeOrRef = StringHelper.before(factoryMethod, ":"); + if (typeOrRef != null) { + // use another class with factory method + factoryMethod = StringHelper.after(factoryMethod, ":"); + // special to support factory method parameters + Object existing = camelContext.getRegistry().lookupByName(typeOrRef); + if (existing != null) { + fc = existing.getClass(); + } else { + fc = camelContext.getClassResolver().resolveMandatoryClass(typeOrRef); + } + } + } + final Class<?> factoryClass = fc; final String fqn = className; final String fm = factoryMethod; final String fp = parameters; @@ -642,9 +658,10 @@ public class DefaultModel implements Model { if (fm != null) { if (fp != null) { // special to support factory method parameters - local = PropertyBindingSupport.newInstanceFactoryParameters(camelContext, clazz, fm, params); + Class<?> target = factoryClass != null ? factoryClass : clazz; + local = PropertyBindingSupport.newInstanceFactoryParameters(camelContext, target, fm, params); } else { - local = camelContext.getInjector().newInstance(clazz, fm); + local = camelContext.getInjector().newInstance(clazz, factoryClass, fm); } if (local == null) { throw new IllegalStateException( diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/app/RegistryBeanDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/app/RegistryBeanDefinition.java index 39b34c1ff95..70100e769cd 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/app/RegistryBeanDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/app/RegistryBeanDefinition.java @@ -48,6 +48,8 @@ public class RegistryBeanDefinition implements ResourceAware { private String type; @XmlAttribute private String factoryMethod; + @XmlAttribute + private String factoryBean; @XmlElement(name = "constructors") @XmlJavaTypeAdapter(BeanConstructorsAdapter.class) private Map<Integer, Object> constructors; @@ -79,6 +81,14 @@ public class RegistryBeanDefinition implements ResourceAware { this.factoryMethod = factoryMethod; } + public String getFactoryBean() { + return factoryBean; + } + + public void setFactoryBean(String factoryBean) { + this.factoryBean = factoryBean; + } + public Map<Integer, Object> getConstructors() { return constructors; } diff --git a/core/camel-core-xml/src/test/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBeanTest.java b/core/camel-core-xml/src/test/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBeanTest.java index 4c112f03c79..524efd90cc7 100644 --- a/core/camel-core-xml/src/test/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBeanTest.java +++ b/core/camel-core-xml/src/test/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBeanTest.java @@ -74,6 +74,11 @@ public class AbstractCamelContextFactoryBeanTest { return null; } + @Override + public <T> T newInstance(Class<T> type, Class<?> factoryClass, String factoryMethod) { + return null; + } + @Override public <T> T newInstance(Class<T> type, boolean postProcessBean) { return ObjectHelper.newInstance(type); diff --git a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java index 5c21ba511f4..f308f407b09 100644 --- a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java @@ -408,6 +408,11 @@ public class PropertyBindingSupportTest extends ContextTestSupport { return null; } + @Override + public <T> T newInstance(Class<T> type, Class<?> factoryClass, String factoryMethod) { + return null; + } + @Override public <T> T newInstance(Class<T> type, boolean postProcessBean) { return null; diff --git a/core/camel-core/src/test/java/org/apache/camel/util/ReflectionInjector.java b/core/camel-core/src/test/java/org/apache/camel/util/ReflectionInjector.java index 70b6b608de3..b363babc892 100644 --- a/core/camel-core/src/test/java/org/apache/camel/util/ReflectionInjector.java +++ b/core/camel-core/src/test/java/org/apache/camel/util/ReflectionInjector.java @@ -25,8 +25,11 @@ import org.apache.camel.support.ObjectHelper; /** * A simple implementation of {@link Injector} which just uses reflection to instantiate new objects using their zero - * argument constructor. For more complex implementations try the Spring or CDI implementations. + * argument constructor. + * + * Use {@link org.apache.camel.impl.engine.DefaultInjector} instead. */ +@Deprecated public class ReflectionInjector implements Injector { @Override @@ -40,7 +43,8 @@ public class ReflectionInjector implements Injector { try { // lookup factory method Method fm = type.getMethod(factoryMethod); - if (Modifier.isStatic(fm.getModifiers()) && Modifier.isPublic(fm.getModifiers()) && fm.getReturnType() == type) { + if (Modifier.isStatic(fm.getModifiers()) && Modifier.isPublic(fm.getModifiers()) + && fm.getReturnType() != Void.TYPE) { answer = (T) fm.invoke(null); } } catch (Exception e) { @@ -49,6 +53,24 @@ public class ReflectionInjector implements Injector { return answer; } + @Override + @SuppressWarnings("unchecked") + public <T> T newInstance(Class<T> type, Class<?> factoryClass, String factoryMethod) { + T answer = null; + try { + // lookup factory method + Method fm = factoryClass.getMethod(factoryMethod); + if (Modifier.isStatic(fm.getModifiers()) && Modifier.isPublic(fm.getModifiers()) + && fm.getReturnType() != Void.TYPE) { + answer = (T) fm.invoke(null); + } + } catch (Exception e) { + throw new RuntimeCamelException( + "Error invoking factory method: " + factoryMethod + " on factory class: " + factoryClass, e); + } + return answer; + } + @Override public <T> T newInstance(Class<T> type, boolean postProcessBean) { return ObjectHelper.newInstance(type); diff --git a/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java b/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java index f48d9c1f23f..2038916ac97 100644 --- a/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java +++ b/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java @@ -42,7 +42,8 @@ public class PropertyBindingSupportClassFactoryMethodTest { .withCamelContext(context) .withTarget(target) .withProperty("name", "Donald") - .withProperty("myDriver", "#class:" + MyDriver.class.getName() + "('localhost:2121', 'scott', 'tiger')") + .withProperty("myDriver", + "#class:" + MyDriver.class.getName() + "#createDriver('localhost:2121', 'scott', 'tiger')") .withRemoveParameters(false).bind(); assertEquals("Donald", target.getName()); @@ -72,7 +73,7 @@ public class PropertyBindingSupportClassFactoryMethodTest { .withTarget(target) .withProperty("name", "Donald") .withProperty("myDriver", - "#class:" + MyDriver.class.getName() + "('{{myUrl}}', '{{myUsername}}', '{{myPassword}}')") + "#class:" + MyDriver.class.getName() + "#createDriver('{{myUrl}}', '{{myUsername}}', '{{myPassword}}')") .withRemoveParameters(false).bind(); assertEquals("Donald", target.getName()); @@ -111,10 +112,12 @@ public class PropertyBindingSupportClassFactoryMethodTest { private String username; private String password; - public MyDriver(String url, String username, String password) { - this.url = url; - this.username = username; - this.password = password; + public static MyDriver createDriver(String url, String username, String password) { + MyDriver driver = new MyDriver(); + driver.url = url; + driver.username = username; + driver.password = password; + return driver; } public String getUrl() { diff --git a/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java b/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportHelperClassFactoryMethodTest.java similarity index 67% copy from core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java copy to core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportHelperClassFactoryMethodTest.java index f48d9c1f23f..ad713f7872a 100644 --- a/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java +++ b/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportHelperClassFactoryMethodTest.java @@ -28,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; /** * Unit test for PropertyBindingSupport */ -public class PropertyBindingSupportClassFactoryMethodTest { +public class PropertyBindingSupportHelperClassFactoryMethodTest { @Test public void testFactory() throws Exception { @@ -42,7 +42,9 @@ public class PropertyBindingSupportClassFactoryMethodTest { .withCamelContext(context) .withTarget(target) .withProperty("name", "Donald") - .withProperty("myDriver", "#class:" + MyDriver.class.getName() + "('localhost:2121', 'scott', 'tiger')") + .withProperty("myDriver", "#class:" + MyDriver.class.getName() + + "#" + MyDriverHelper.class.getName() + + ":createDriver('localhost:2121', 'scott', 'tiger')") .withRemoveParameters(false).bind(); assertEquals("Donald", target.getName()); @@ -72,7 +74,35 @@ public class PropertyBindingSupportClassFactoryMethodTest { .withTarget(target) .withProperty("name", "Donald") .withProperty("myDriver", - "#class:" + MyDriver.class.getName() + "('{{myUrl}}', '{{myUsername}}', '{{myPassword}}')") + "#class:" + MyDriver.class.getName() + + "#" + MyDriverHelper.class.getName() + + ":createDriver('{{myUrl}}', '{{myUsername}}', '{{myPassword}}')") + .withRemoveParameters(false).bind(); + + assertEquals("Donald", target.getName()); + assertEquals("localhost:2121", target.getMyDriver().getUrl()); + assertEquals("scott", target.getMyDriver().getUsername()); + assertEquals("tiger", target.getMyDriver().getPassword()); + + context.stop(); + } + + @Test + public void testFactoryRef() throws Exception { + CamelContext context = new DefaultCamelContext(); + + context.getRegistry().bind("myDriverHelper", new MyDriverHelper()); + + context.start(); + + MyApp target = new MyApp(); + + PropertyBindingSupport.build() + .withCamelContext(context) + .withTarget(target) + .withProperty("name", "Donald") + .withProperty("myDriver", "#class:" + MyDriver.class.getName() + + "#myDriverHelper:createDriver('localhost:2121', 'scott', 'tiger')") .withRemoveParameters(false).bind(); assertEquals("Donald", target.getName()); @@ -105,18 +135,24 @@ public class PropertyBindingSupportClassFactoryMethodTest { } } + public static class MyDriverHelper { + + public static MyDriver createDriver(String url, String username, String password) { + MyDriver driver = new MyDriver(); + driver.url = url; + driver.username = username; + driver.password = password; + return driver; + } + + } + public static class MyDriver { private String url; private String username; private String password; - public MyDriver(String url, String username, String password) { - this.url = url; - this.username = username; - this.password = password; - } - public String getUrl() { return url; } diff --git a/core/camel-support/src/main/java/org/apache/camel/support/EndpointHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/EndpointHelper.java index c37820caa3f..11fba313980 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/EndpointHelper.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/EndpointHelper.java @@ -377,11 +377,27 @@ public final class EndpointHelper { className = StringHelper.before(className, "#"); } Class<?> clazz = camelContext.getClassResolver().resolveMandatoryClass(className); + Class<?> factoryClass = null; + if (factoryMethod != null) { + String typeOrRef = StringHelper.before(factoryMethod, ":"); + if (typeOrRef != null) { + // use another class with factory method + factoryMethod = StringHelper.after(factoryMethod, ":"); + // special to support factory method parameters + Object existing = camelContext.getRegistry().lookupByName(typeOrRef); + if (existing != null) { + factoryClass = existing.getClass(); + } else { + factoryClass = camelContext.getClassResolver().resolveMandatoryClass(typeOrRef); + } + } + } if (factoryMethod != null && parameters != null) { - answer = PropertyBindingSupport.newInstanceFactoryParameters(camelContext, clazz, factoryMethod, parameters); + Class<?> target = factoryClass != null ? factoryClass : clazz; + answer = PropertyBindingSupport.newInstanceFactoryParameters(camelContext, target, factoryMethod, parameters); } else if (factoryMethod != null) { - answer = camelContext.getInjector().newInstance(type, factoryMethod); + answer = camelContext.getInjector().newInstance(type, factoryClass, factoryMethod); } else if (parameters != null) { answer = PropertyBindingSupport.newInstanceConstructorParameters(camelContext, clazz, parameters); } else { 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 986cd2180d9..c8153a261fe 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 @@ -70,8 +70,10 @@ import static org.apache.camel.util.StringHelper.startsWithIgnoreCase; * the instance via a factory method then you specify the method as shown: #class:com.foo.MyClassType#myFactoryMethod. * And if the factory method requires parameters they can be specified as follows: * #class:com.foo.MyClassType#myFactoryMethod('Hello World', 5, true). Or if you need to create the instance via - * constructor parameters then you can specify the parameters as shown: #class:com.foo.MyClass('Hello World', 5, - * true)</li>. + * constructor parameters then you can specify the parameters as shown: #class:com.foo.MyClass('Hello World', 5, true). + * If the factory method is on another bean or class, then you must specify this as shown: + * #class:com.foo.MyClassType#com.foo.MyFactory:myFactoryMethod. Where com.foo.MyFactory either refers to an class name, + * or can refer to an existing bean by id, such as: #class:com.foo.MyClassType#myFactoryBean:myFactoryMethod.</li>. * <li>valueAs(type):value</li> - To declare that the value should be converted to the given type, such as * #valueAs(int):123 which indicates that the value 123 should be converted to an integer. * <li>ignore case - Whether to ignore case for property keys</li> @@ -1372,11 +1374,26 @@ public final class PropertyBindingSupport { for (int i = 0; i < found.getParameterCount(); i++) { Class<?> paramType = found.getParameterTypes()[i]; Object param = params[i]; - Object val = camelContext.getTypeConverter().convertTo(paramType, param); + Object val = null; + // special as we may refer to other #bean or #type in the parameter + if (param instanceof String) { + String str = param.toString(); + if (str.startsWith("#")) { + Object bean = resolveBean(camelContext, param); + if (bean != null) { + val = bean; + } + } + } // unquote text if (val instanceof String) { val = StringHelper.removeLeadingAndEndingQuotes((String) val); } + if (val != null) { + val = camelContext.getTypeConverter().tryConvertTo(paramType, val); + } else { + val = camelContext.getTypeConverter().convertTo(paramType, param); + } arr[i] = val; } @@ -1558,11 +1575,24 @@ public final class PropertyBindingSupport { } Class<?> type = camelContext.getClassResolver().resolveMandatoryClass(className); if (factoryMethod != null) { - if (parameters != null) { + Class<?> factoryClass = null; + String typeOrRef = StringHelper.before(factoryMethod, ":"); + if (typeOrRef != null) { + // use another class with factory method + factoryMethod = StringHelper.after(factoryMethod, ":"); // special to support factory method parameters - answer = newInstanceFactoryParameters(camelContext, type, factoryMethod, parameters); + Object existing = camelContext.getRegistry().lookupByName(typeOrRef); + if (existing != null) { + factoryClass = existing.getClass(); + } else { + factoryClass = camelContext.getClassResolver().resolveMandatoryClass(typeOrRef); + } + } + if (parameters != null) { + Class<?> target = factoryClass != null ? factoryClass : type; + answer = newInstanceFactoryParameters(camelContext, target, factoryMethod, parameters); } else { - answer = camelContext.getInjector().newInstance(type, factoryMethod); + answer = camelContext.getInjector().newInstance(type, factoryClass, factoryMethod); } if (answer == null) { throw new IllegalStateException( diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java index 7d49154c5ad..7dcff72a36b 100644 --- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java +++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java @@ -1634,6 +1634,7 @@ public class ModelParser extends BaseParser { protected RegistryBeanDefinition doParseRegistryBeanDefinition() throws IOException, XmlPullParserException { return doParse(new RegistryBeanDefinition(), (def, key, val) -> { switch (key) { + case "factoryBean": def.setFactoryBean(val); break; case "factoryMethod": def.setFactoryMethod(val); break; case "name": def.setName(val); break; case "type": def.setType(val); break; diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java index 2c2ef105ef5..4e228274c16 100644 --- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java +++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java @@ -2569,6 +2569,7 @@ public class ModelWriter extends BaseWriter { doWriteAttribute("factoryMethod", def.getFactoryMethod()); doWriteAttribute("name", def.getName()); doWriteAttribute("type", def.getType()); + doWriteAttribute("factoryBean", def.getFactoryBean()); doWriteElement("constructors", new BeanConstructorsAdapter().marshal(def.getConstructors()), this::doWriteBeanConstructorsDefinition); doWriteElement("properties", new BeanPropertiesAdapter().marshal(def.getProperties()), this::doWriteBeanPropertiesDefinition); endElement(name); diff --git a/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java b/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java index 252c86dfce2..7cec1ce562b 100644 --- a/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java +++ b/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java @@ -324,12 +324,14 @@ public class LwModelToXMLDumper implements ModelToXMLDumper { if (type.startsWith("#class:")) { type = type.substring(7); } - String factoryMethod = b.getFactoryMethod(); - if (factoryMethod != null) { - buffer.write(String.format(" <bean name=\"%s\" type=\"%s\" factoryMethod=\"%s\">%n", b.getName(), type, factoryMethod)); - } else { - buffer.write(String.format(" <bean name=\"%s\" type=\"%s\">%n", b.getName(), type)); + buffer.write(String.format(" <bean name=\"%s\" type=\"%s\"", b.getName(), type)); + if (b.getFactoryBean() != null) { + buffer.write(String.format(" factory-bean=\"%s\"", b.getFactoryBean())); } + if (b.getFactoryMethod() != null) { + buffer.write(String.format(" factory-method=\"%s\"", b.getFactoryMethod())); + } + buffer.write(">\n"); if (b.getConstructors() != null && !b.getConstructors().isEmpty()) { buffer.write(String.format(" <constructors>%n")); for (Map.Entry<Integer, Object> entry : b.getConstructors().entrySet()) { diff --git a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java index 8fa8b190bc9..02e3078c6b1 100644 --- a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java +++ b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java @@ -337,12 +337,15 @@ public class JaxbModelToXMLDumper implements ModelToXMLDumper { if (type.startsWith("#class:")) { type = type.substring(7); } + buffer.write(String.format(" <bean name=\"%s\" type=\"%s\"", b.getName(), type)); String factoryMethod = b.getFactoryMethod(); - if (factoryMethod != null) { - buffer.write(String.format(" <bean name=\"%s\" type=\"%s\" factoryMethod=\"%s\">%n", b.getName(), type, factoryMethod)); - } else { - buffer.write(String.format(" <bean name=\"%s\" type=\"%s\">%n", b.getName(), type)); + if (b.getFactoryBean() != null) { + buffer.write(String.format(" factory-bean=\"%s\"", b.getFactoryBean())); } + if (b.getFactoryMethod() != null) { + buffer.write(String.format(" factory-method=\"%s\"", b.getFactoryMethod())); + } + buffer.write(">\n"); if (b.getConstructors() != null && !b.getConstructors().isEmpty()) { buffer.write(String.format(" <constructors>%n")); for (Map.Entry<Integer, Object> entry : b.getConstructors().entrySet()) { diff --git a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java index 09249ae2272..08c098e8cf0 100644 --- a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java +++ b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java @@ -2569,6 +2569,7 @@ public class ModelWriter extends BaseWriter { doWriteAttribute("factoryMethod", def.getFactoryMethod()); doWriteAttribute("name", def.getName()); doWriteAttribute("type", def.getType()); + doWriteAttribute("factoryBean", def.getFactoryBean()); doWriteElement("constructors", new BeanConstructorsAdapter().marshal(def.getConstructors()), this::doWriteBeanConstructorsDefinition); doWriteElement("properties", new BeanPropertiesAdapter().marshal(def.getProperties()), this::doWriteBeanPropertiesDefinition); endElement(name); diff --git a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java index 7cca44385c9..2507043724a 100644 --- a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java +++ b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java @@ -312,11 +312,13 @@ public class LwModelToYAMLDumper implements ModelToYAMLDumper { if (type.startsWith("#class:")) { type = type.substring(7); } - String factoryMethod = b.getFactoryMethod(); buffer.write(String.format(" - name: %s%n", b.getName())); buffer.write(String.format(" type: \"%s\"%n", type)); - if (factoryMethod != null) { - buffer.write(String.format(" factoryMethod: \"%s\"%n", factoryMethod)); + if (b.getFactoryBean() != null) { + buffer.write(String.format(" factoryBean: \"%s\"%n", b.getFactoryBean())); + } + if (b.getFactoryMethod() != null) { + buffer.write(String.format(" factoryMethod: \"%s\"%n", b.getFactoryMethod())); } if (b.getConstructors() != null && !b.getConstructors().isEmpty()) { buffer.write(String.format(" constructors:%n")); diff --git a/docs/user-manual/modules/ROOT/pages/property-binding.adoc b/docs/user-manual/modules/ROOT/pages/property-binding.adoc index 267e87a3fae..8687eee222c 100644 --- a/docs/user-manual/modules/ROOT/pages/property-binding.adoc +++ b/docs/user-manual/modules/ROOT/pages/property-binding.adoc @@ -28,6 +28,9 @@ The `PropertyBindingSupport` class supports binding String valued properties to `#class:com.foo.MyClassType#myFactoryMethod('Hello World', 5, true)`. Or if you need to create the instance via constructor parameters then you can specify the parameters as shown: `#class:com.foo.MyClass('Hello World', 5, true)`. + If the factory method is on another bean or class, then you must specify this as shown: + `#class:com.foo.MyClassType#com.foo.MyFactory:myFactoryMethod`. Where `com.foo.MyFactory` either refers to a FQN classname, + or can refer to an existing bean by id, such as: `#class:com.foo.MyClassType#myFactoryBean:myFactoryMethod`. - _valueAs(type):value_ - To declare that the value should be converted to the given type, such as `#valueAs(int):123` which indicates that the value 123 should be converted to an integer. - _ignore case_ - Whether to ignore case for property keys (will ignore by default) diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/KameletMainInjector.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/KameletMainInjector.java index c1a691db61e..8e8a7449bdd 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/KameletMainInjector.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/KameletMainInjector.java @@ -56,6 +56,15 @@ public class KameletMainInjector implements Injector { return delegate.newInstance(type, factoryMethod); } + @Override + public <T> T newInstance(Class<T> type, Class<?> factoryClass, String factoryMethod) { + boolean accept = acceptComponent(type); + if (!accept) { + return (T) delegate.newInstance(StubComponent.class); + } + return delegate.newInstance(type, factoryClass, factoryMethod); + } + @Override public <T> T newInstance(Class<T> type, boolean postProcessBean) { boolean accept = acceptComponent(type); diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java index 5548661cae2..cba1329dd65 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java @@ -107,7 +107,11 @@ public class BlueprintXmlBeansHandler { rrd.setType(XmlHelper.getAttribute(node, "class")); rrd.setName(name); - // factory method + // factory bean/method + String fb = XmlHelper.getAttribute(node, "factory-ref"); + if (fb != null) { + rrd.setFactoryBean(fb); + } String fm = XmlHelper.getAttribute(node, "factory-method"); if (fm != null) { rrd.setFactoryMethod(fm); @@ -210,8 +214,10 @@ public class BlueprintXmlBeansHandler { type = "#class:" + type; } try { - // factory method - if (def.getFactoryMethod() != null) { + // factory bean/method + if (def.getFactoryBean() != null && def.getFactoryMethod() != null) { + type = type + "#" + def.getFactoryBean() + ":" + def.getFactoryMethod(); + } else if (def.getFactoryMethod() != null) { type = type + "#" + def.getFactoryMethod(); } // property binding support has constructor arguments as part of the type diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java index 61ad9c44fa8..b371c5a2ac0 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java @@ -236,7 +236,10 @@ public class SpringXmlBeansHandler { rrd.setName(name); model.addRegistryBean(rrd); - // factory method + // factory bean/method + if (def.getFactoryBeanName() != null) { + rrd.setFactoryBean(def.getFactoryBeanName()); + } if (def.getFactoryMethodName() != null) { rrd.setFactoryMethod(def.getFactoryMethodName()); } diff --git a/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc b/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc index 4b1e029eff7..f96371e8a0f 100644 --- a/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc +++ b/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc @@ -188,6 +188,40 @@ public class MyBean { NOTE: The factory method must be `public static` and from the same class as the created class itself. +=== Creating beans from factory bean + +A bean can also be created from a factory bean as shown below: + +[source,xml] +---- + <bean name="myBean" type="com.acme.MyBean" + factoryBean="com.acme.MyHelper" factoryMethod="createMyBean"> + <constructors> + <constructor index="0" value="true"/> + <constructor index="1" value="Hello World"/> + </constructors> + </bean> +---- + +TIP: `factoryBean` can also refer to an existing bean by bean id instead of FQN classname. + +When using `factoryBean` and `factoryMethod` then the arguments to this method is taken from `constructors`. +So in the example above, this means that class `com.acme.MyHelper` should be as follows: + +[source,java] +---- +public class MyHelper { + + public static MyBean createMyBean(boolean important, String message) { + MyBean answer = ... + // create and configure the bean + return answer; + } +} +---- + +NOTE: The factory method must be `public static`. + == See Also diff --git a/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java b/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java index 541eda03ae2..604378a65ba 100644 --- a/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java +++ b/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java @@ -327,8 +327,10 @@ public class XmlRoutesBuilderLoader extends RouteBuilderLoaderSupport { if (type != null && !type.startsWith("#")) { type = "#class:" + type; try { - // factory method - if (def.getFactoryMethod() != null) { + // factory bean/method + if (def.getFactoryBean() != null && def.getFactoryMethod() != null) { + type = type + "#" + def.getFactoryBean() + ":" + def.getFactoryMethod(); + } else if (def.getFactoryMethod() != null) { type = type + "#" + def.getFactoryMethod(); } // property binding support has constructor arguments as part of the type diff --git a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadAppTest.java b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadAppTest.java index 59dddb4ef9d..3f031dccd43 100644 --- a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadAppTest.java +++ b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadAppTest.java @@ -145,7 +145,7 @@ public class XmlLoadAppTest { } @Test - public void testLoadCamelAppWithBeanFactory() throws Exception { + public void testLoadCamelAppWithBeanFactoryMethod() throws Exception { try (DefaultCamelContext context = new DefaultCamelContext()) { context.start(); @@ -167,4 +167,27 @@ public class XmlLoadAppTest { } } + @Test + public void testLoadCamelAppWithBeanFactoryBeanAndMethod() throws Exception { + try (DefaultCamelContext context = new DefaultCamelContext()) { + context.start(); + + Resource resource = PluginHelper.getResourceLoader(context).resolveResource( + "/org/apache/camel/dsl/xml/io/camel-app7.xml"); + + RoutesLoader routesLoader = PluginHelper.getRoutesLoader(context); + routesLoader.preParseRoute(resource, false); + routesLoader.loadRoutes(resource); + + assertNotNull(context.getRoute("r7"), "Loaded r7 route should be there"); + assertEquals(1, context.getRoutes().size()); + + // test that loaded route works + MockEndpoint y7 = context.getEndpoint("mock:y7", MockEndpoint.class); + y7.expectedBodiesReceived("Hello Pluto. I am Camel and 44 years old!"); + context.createProducerTemplate().sendBody("direct:x7", "Pluto"); + y7.assertIsSatisfied(); + } + } + } diff --git a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java index 3db2c39de10..f226415da8b 100644 --- a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java +++ b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java @@ -26,7 +26,7 @@ public class MyFacBean { return new MyFacBean(field1, field2); } - private MyFacBean(String field1, String field2) { + MyFacBean(String field1, String field2) { this.field1 = field1; this.field2 = field2; } diff --git a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacHelper.java similarity index 62% copy from dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java copy to dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacHelper.java index 3db2c39de10..e27b943eafc 100644 --- a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java +++ b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacHelper.java @@ -16,39 +16,10 @@ */ package org.apache.camel.dsl.xml.io.beans; -public class MyFacBean { - - private String field1; - private String field2; - private int age; +public class MyFacHelper { public static MyFacBean createBean(String field1, String field2) { return new MyFacBean(field1, field2); } - private MyFacBean(String field1, String field2) { - this.field1 = field1; - this.field2 = field2; - } - - public String getField1() { - return field1; - } - - public String getField2() { - return field2; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public String hi(String body) { - return field1 + " " + body + ". I am " + field2 + " and " + age + " years old!"; - } - } diff --git a/dsl/camel-xml-io-dsl/src/test/resources/org/apache/camel/dsl/xml/io/camel-app7.xml b/dsl/camel-xml-io-dsl/src/test/resources/org/apache/camel/dsl/xml/io/camel-app7.xml new file mode 100644 index 00000000000..396f9768418 --- /dev/null +++ b/dsl/camel-xml-io-dsl/src/test/resources/org/apache/camel/dsl/xml/io/camel-app7.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<camel xmlns="http://camel.apache.org/schema/spring" xmlns:s="http://www.springframework.org/schema/beans"> + + <bean name="myHelper" type="org.apache.camel.dsl.xml.io.beans.MyFacHelper"/> + + <bean name="xml-bean-from-registry" type="org.apache.camel.dsl.xml.io.beans.MyFacBean" + factoryBean="myHelper" factoryMethod="createBean"> + <constructors> + <constructor value="Hello"/> + <constructor value="Camel"/> + </constructors> + <properties> + <property key="age" value="44"/> + </properties> + </bean> + + <route id="r7"> + <from uri="direct:x7"/> + <bean ref="xml-bean-from-registry" method="hi"/> + <to uri="mock:y7"/> + </route> + +</camel> diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java index b8e5b914ec2..45c0068500c 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java @@ -12349,6 +12349,7 @@ public final class ModelDeserializers extends YamlDeserializerSupport { order = org.apache.camel.dsl.yaml.common.YamlDeserializerResolver.ORDER_LOWEST - 1, properties = { @YamlProperty(name = "constructors", type = "object"), + @YamlProperty(name = "factory-bean", type = "string"), @YamlProperty(name = "factory-method", type = "string"), @YamlProperty(name = "name", type = "string", required = true), @YamlProperty(name = "properties", type = "object"), @@ -12374,6 +12375,11 @@ public final class ModelDeserializers extends YamlDeserializerSupport { target.setConstructors(val); break; } + case "factory-bean": { + String val = asText(node); + target.setFactoryBean(val); + break; + } case "factory-method": { String val = asText(node); target.setFactoryMethod(val); diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/BeansDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/BeansDeserializer.java index 917ea258fd3..905d5a4ce21 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/BeansDeserializer.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/BeansDeserializer.java @@ -91,19 +91,21 @@ public class BeansDeserializer extends YamlDeserializerSupport implements Constr beanCache.clear(); } - public Object newInstance(RegistryBeanDefinition bean, CamelContext context) throws Exception { + public Object newInstance(RegistryBeanDefinition def, CamelContext context) throws Exception { - String type = bean.getType(); + String type = def.getType(); - // factory method - if (bean.getFactoryMethod() != null) { - type = type + "#" + bean.getFactoryMethod(); + // factory bean/method + if (def.getFactoryBean() != null && def.getFactoryMethod() != null) { + type = type + "#" + def.getFactoryBean() + ":" + def.getFactoryMethod(); + } else if (def.getFactoryMethod() != null) { + type = type + "#" + def.getFactoryMethod(); } // property binding support has constructor arguments as part of the type StringJoiner ctr = new StringJoiner(", "); - if (bean.getConstructors() != null && !bean.getConstructors().isEmpty()) { + if (def.getConstructors() != null && !def.getConstructors().isEmpty()) { // need to sort constructor args based on index position - Map<Integer, Object> sorted = new TreeMap<>(bean.getConstructors()); + Map<Integer, Object> sorted = new TreeMap<>(def.getConstructors()); for (Object val : sorted.values()) { String text = val.toString(); if (!StringHelper.isQuoted(text)) { @@ -116,8 +118,8 @@ public class BeansDeserializer extends YamlDeserializerSupport implements Constr final Object target = PropertyBindingSupport.resolveBean(context, type); - if (bean.getProperties() != null && !bean.getProperties().isEmpty()) { - PropertyBindingSupport.setPropertiesOnTarget(context, target, bean.getProperties()); + if (def.getProperties() != null && !def.getProperties().isEmpty()) { + PropertyBindingSupport.setPropertiesOnTarget(context, target, def.getProperties()); } return target; diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json index 7a3c7e0c5fc..4c298bdbda4 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json @@ -8026,6 +8026,9 @@ "constructors" : { "type" : "object" }, + "factoryBean" : { + "type" : "string" + }, "factoryMethod" : { "type" : "string" }, diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc index 49c68aca270..71fd6f385e6 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc @@ -232,6 +232,41 @@ public class MyBean { NOTE: The factory method must be `public static` and from the same class as the created class itself. +=== Creating beans from factory bean + +A bean can also be created from a factory bean as shown below: + +[source,yaml] +---- +- beans: + - name: myBean + type: com.acme.MyBean + factoryBean: com.acme.MyHelper + factoryMethod: createMyBean + constructors: + 0: true + 1: "Hello World" +---- + +TIP: `factoryBean` can also refer to an existing bean by bean id instead of FQN classname. + +When using `factoryBean` and `factoryMethod` then the arguments to this method is taken from `constructors`. +So in the example above, this means that class `com.acme.MyHelper` should be as follows: + +[source,java] +---- +public class MyHelper { + + public static MyBean createMyBean(boolean important, String message) { + MyBean answer = ... + // create and configure the bean + return answer; + } +} +---- + +NOTE: The factory method must be `public static`. + == Configuring options on languages Some xref:components:languages:index.adoc[Languages] have additional configurations diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/BeansTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/BeansTest.groovy index eb24f59d604..1d3209c6627 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/BeansTest.groovy +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/BeansTest.groovy @@ -20,6 +20,7 @@ import org.apache.camel.dsl.yaml.support.YamlTestSupport import org.apache.camel.dsl.yaml.support.model.MyBean import org.apache.camel.dsl.yaml.support.model.MyCtrBean import org.apache.camel.dsl.yaml.support.model.MyFacBean +import org.apache.camel.dsl.yaml.support.model.MyFacHelper class BeansTest extends YamlTestSupport { @@ -154,5 +155,28 @@ class BeansTest extends YamlTestSupport { } } + def "beans with factory helper"() { + when: + loadRoutes """ + - beans: + - name: myFac + type: ${MyFacBean.class.name} + factoryBean: ${MyFacHelper.class.name} + factoryMethod: createBean + constructors: + 0: 'fac1' + 1: 'fac2' + properties: + age: 43 + """ + + then: + with(context.registry.lookupByName('myFac'), MyFacBean) { + it.field1 == 'fac1' + it.field2 == 'fac2' + it.age == 43 + } + } + } diff --git a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/model/MyFacHelper.groovy similarity index 54% copy from dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java copy to dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/model/MyFacHelper.groovy index 3db2c39de10..93e19d044ed 100644 --- a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/model/MyFacHelper.groovy @@ -14,41 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.dsl.xml.io.beans; +package org.apache.camel.dsl.yaml.support.model -public class MyFacBean { +class MyFacHelper { - private String field1; - private String field2; - private int age; - - public static MyFacBean createBean(String field1, String field2) { + static MyFacBean createBean(String field1, String field2) { return new MyFacBean(field1, field2); } - private MyFacBean(String field1, String field2) { - this.field1 = field1; - this.field2 = field2; - } - - public String getField1() { - return field1; - } - - public String getField2() { - return field2; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public String hi(String body) { - return field1 + " " + body + ". I am " + field2 + " and " + age + " years old!"; - } - }
