Skip to content

Commit

Permalink
CAMEL-19386: Support object property on template bean (#10205)
Browse files Browse the repository at this point in the history
Also fixed the annotation for `beans` property on TemplatedRouteDefinitionDeserializer and RouteTemplateDefinitionDeserializer to represent what deserializers actually supports in the generated schema
  • Loading branch information
igarashitm committed May 25, 2023
1 parent 9a7a922 commit be5dd92
Show file tree
Hide file tree
Showing 16 changed files with 98 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
public class MyLocalBean {

private String prefix;
private String suffix;

public String getPrefix() {
return prefix;
Expand All @@ -28,7 +29,15 @@ public void setPrefix(String prefix) {
this.prefix = prefix;
}

public String getSuffix() {
return suffix;
}

public void setSuffix(String suffix) {
this.suffix = suffix;
}

public String hello(String body) {
return prefix + " " + body;
return prefix + " " + body + suffix;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ public void testLocalBean() throws Exception {
parameters.put("foo", "one");
parameters.put("bar", "cheese");
parameters.put("greeting", "Davs");
parameters.put("suffix", "!!!");
context.addRouteFromTemplate("first", "myTemplate", parameters);

MockEndpoint mock = getMockEndpoint("mock:cheese");
mock.expectedBodiesReceived("Davs World");
mock.expectedBodiesReceived("Davs World!!!");

template.sendBody("direct:one", "World");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@
<templateParameter name="foo"/>
<templateParameter name="bar"/>
<templateParameter name="greeting"/>
<templateParameter name="suffix"/>
<templateBean name="myBean" type="#class:org.apache.camel.spring.routebuilder.MyLocalBean">

<property key="prefix" value="{{greeting}}"/>
<properties>
<property key="suffix" value="{{suffix}}"/>
</properties>
</templateBean>
<route>
<from uri="direct:{{foo}}"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,10 @@ private static void bind(BeanFactoryDefinition<?, ?> beanFactory, RouteTemplateC
throws Exception {
final Map<String, Object> props = new HashMap<>();
if (beanFactory.getProperties() != null) {
beanFactory.getProperties().forEach(p -> props.put(p.getKey(), p.getValue()));
props.putAll(beanFactory.getProperties());
}
if (beanFactory.getPropertyDefinitions() != null) {
beanFactory.getPropertyDefinitions().forEach(p -> props.put(p.getKey(), p.getValue()));
}
if (beanFactory.getBeanSupplier() != null) {
if (props.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"type": { "index": 1, "kind": "attribute", "displayName": "Type", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "What type to use for creating the bean. Can be one of: #class,#type,bean,groovy,joor,language,mvel,ognl. #class or #type then the bean is created via the fully qualified classname, such as #class:com.foo.MyBean The others are scripting languages that gives more power to create the bean with an inlined code in the script section, such as using groovy." },
"beanType": { "index": 2, "kind": "attribute", "displayName": "Bean Type", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To set the type (fully qualified class name) of the returned bean created by the script. Knowing the type of the bean can be needed when dependency injection by type is in use, or when looking in registry via class type." },
"property": { "index": 3, "kind": "element", "displayName": "Property", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Optional properties to set on the created local bean" },
"script": { "index": 4, "kind": "element", "displayName": "Script", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The script to execute that creates the bean when using scripting languages. If the script use the prefix resource: such as resource:classpath:com\/foo\/myscript.groovy, resource:file:\/var\/myscript.groovy, then its loaded from the external resource." }
"properties": { "index": 4, "kind": "element", "displayName": "Properties", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "deprecated": false, "autowired": false, "secret": false, "description": "Optional properties to set on the created local bean" },
"script": { "index": 5, "kind": "element", "displayName": "Script", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The script to execute that creates the bean when using scripting languages. If the script use the prefix resource: such as resource:classpath:com\/foo\/myscript.groovy, resource:file:\/var\/myscript.groovy, then its loaded from the external resource." }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"type": { "index": 1, "kind": "attribute", "displayName": "Type", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "What type to use for creating the bean. Can be one of: #class,#type,bean,groovy,joor,language,mvel,ognl. #class or #type then the bean is created via the fully qualified classname, such as #class:com.foo.MyBean The others are scripting languages that gives more power to create the bean with an inlined code in the script section, such as using groovy." },
"beanType": { "index": 2, "kind": "attribute", "displayName": "Bean Type", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To set the type (fully qualified class name) of the returned bean created by the script. Knowing the type of the bean can be needed when dependency injection by type is in use, or when looking in registry via class type." },
"property": { "index": 3, "kind": "element", "displayName": "Property", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Optional properties to set on the created local bean" },
"script": { "index": 4, "kind": "element", "displayName": "Script", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The script to execute that creates the bean when using scripting languages. If the script use the prefix resource: such as resource:classpath:com\/foo\/myscript.groovy, resource:file:\/var\/myscript.groovy, then its loaded from the external resource." }
"properties": { "index": 4, "kind": "element", "displayName": "Properties", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "deprecated": false, "autowired": false, "secret": false, "description": "Optional properties to set on the created local bean" },
"script": { "index": 5, "kind": "element", "displayName": "Script", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The script to execute that creates the bean when using scripting languages. If the script use the prefix resource: such as resource:classpath:com\/foo\/myscript.groovy, resource:file:\/var\/myscript.groovy, then its loaded from the external resource." }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
package org.apache.camel.model;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

Expand All @@ -25,8 +25,10 @@
import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlTransient;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

import org.apache.camel.RouteTemplateContext;
import org.apache.camel.model.app.BeanPropertiesAdapter;
import org.apache.camel.spi.Metadata;

/**
Expand Down Expand Up @@ -56,7 +58,10 @@ public abstract class BeanFactoryDefinition<
@Metadata(label = "advanced")
private String beanType;
@XmlElement(name = "property")
private List<PropertyDefinition> properties;
private List<PropertyDefinition> propertyDefinitions;
@XmlElement(name = "properties")
@XmlJavaTypeAdapter(BeanPropertiesAdapter.class)
private Map<String, Object> properties;
@XmlElement(name = "script")
@Metadata(label = "advanced")
private String script;
Expand Down Expand Up @@ -120,17 +125,35 @@ public Class<?> getBeanClass() {
return beanClass;
}

public List<PropertyDefinition> getProperties() {
public Map<String, Object> getProperties() {
return properties;
}

/**
* Optional properties to set on the created local bean
*/
public void setProperties(List<PropertyDefinition> properties) {
public void setProperties(Map<String, Object> properties) {
this.properties = properties;
}

public List<PropertyDefinition> getPropertyDefinitions() {
return propertyDefinitions;
}

/**
* Optional properties to set on the created local bean
*/
public void setPropertyDefinitions(List<PropertyDefinition> propertyDefinitions) {
this.propertyDefinitions = propertyDefinitions;
}

public void addProperty(PropertyDefinition property) {
if (propertyDefinitions == null) {
propertyDefinitions = new LinkedList<>();
}
propertyDefinitions.add(property);
}

public RouteTemplateContext.BeanSupplier<Object> getBeanSupplier() {
return beanSupplier;
}
Expand Down Expand Up @@ -354,22 +377,19 @@ public P ognl(String script) {
*/
@SuppressWarnings("unchecked")
public T property(String key, String value) {
if (properties == null) {
properties = new ArrayList<>();
if (propertyDefinitions == null) {
propertyDefinitions = new LinkedList<>();
}
properties.add(new PropertyDefinition(key, value));
propertyDefinitions.add(new PropertyDefinition(key, value));
return (T) this;
}

/**
* Sets properties to set on the created local bean
*/
@SuppressWarnings("unchecked")
public T properties(Map<String, String> properties) {
if (this.properties == null) {
this.properties = new ArrayList<>();
}
properties.forEach((k, v) -> this.properties.add(new PropertyDefinition(k, v)));
public T properties(Map<String, Object> properties) {
this.properties = properties;
return (T) this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ public class BeanPropertiesAdapter extends XmlAdapter<BeanPropertiesDefinition,

@Override
public Map<String, Object> unmarshal(BeanPropertiesDefinition v) {
if (v == null) {
return null;
}
Map<String, Object> result = new LinkedHashMap<>();
for (BeanPropertyDefinition pd : v.getProperties()) {
if (pd.getProperties() != null) {
Expand All @@ -41,6 +44,9 @@ public Map<String, Object> unmarshal(BeanPropertiesDefinition v) {
@Override
@SuppressWarnings("unchecked")
public BeanPropertiesDefinition marshal(Map<String, Object> v) {
if (v == null) {
return null;
}
final BeanPropertyDefinition[] result = new BeanPropertyDefinition[v.size()];
int pos = 0;
for (Map.Entry<String, Object> entry : v.entrySet()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1114,13 +1114,24 @@ protected <T extends BeanFactoryDefinition> AttributeHandler<T> beanFactoryDefin
protected <T extends BeanFactoryDefinition> ElementHandler<T> beanFactoryDefinitionElementHandler() {
return (def, key) -> {
switch (key) {
case "property": doAdd(doParsePropertyDefinition(), def.getProperties(), def::setProperties); break;
case "properties": def.setProperties(new BeanPropertiesAdapter().unmarshal(doParseBeanPropertiesDefinition())); break;
case "property": doAdd(doParsePropertyDefinition(), def.getPropertyDefinitions(), def::setPropertyDefinitions); break;
case "script": def.setScript(doParseText()); break;
default: return false;
}
return true;
};
}
protected BeanPropertiesDefinition doParseBeanPropertiesDefinition() throws IOException, XmlPullParserException {
return doParse(new BeanPropertiesDefinition(),
noAttributeHandler(), (def, key) -> {
if ("property".equals(key)) {
doAdd(doParseBeanPropertyDefinition(), def.getProperties(), def::setProperties);
return true;
}
return false;
}, noValueHandler());
}
protected RouteTemplateContextRefDefinition doParseRouteTemplateContextRefDefinition() throws IOException, XmlPullParserException {
return doParse(new RouteTemplateContextRefDefinition(), (def, key, val) -> {
if ("ref".equals(key)) {
Expand Down Expand Up @@ -1630,16 +1641,6 @@ protected RegistryBeanDefinition doParseRegistryBeanDefinition() throws IOExcept
return false;
}, noValueHandler());
}
protected BeanPropertiesDefinition doParseBeanPropertiesDefinition() throws IOException, XmlPullParserException {
return doParse(new BeanPropertiesDefinition(),
noAttributeHandler(), (def, key) -> {
if ("property".equals(key)) {
doAdd(doParseBeanPropertyDefinition(), def.getProperties(), def::setProperties);
return true;
}
return false;
}, noValueHandler());
}
protected BeanPropertyDefinition doParseBeanPropertyDefinition() throws IOException, XmlPullParserException {
return doParse(new BeanPropertyDefinition(), (def, key, val) -> {
switch (key) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1075,8 +1075,9 @@ protected void doWriteBeanFactoryDefinitionAttributes(
protected void doWriteBeanFactoryDefinitionElements(
BeanFactoryDefinition<?, ?> def)
throws IOException {
doWriteList(null, "property", def.getPropertyDefinitions(), this::doWritePropertyDefinition);
doWriteElement("script", def.getScript(), this::doWriteString);
doWriteList(null, "property", def.getProperties(), this::doWritePropertyDefinition);
doWriteElement("properties", new BeanPropertiesAdapter().marshal(def.getProperties()), this::doWriteBeanPropertiesDefinition);
}
protected void doWriteBeanFactoryDefinition(
String name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
*/
package org.apache.camel.dsl.yaml.deserializers;

import java.util.stream.Collectors;

import org.apache.camel.dsl.yaml.common.YamlDeserializerBase;
import org.apache.camel.model.BeanFactoryDefinition;
import org.apache.camel.model.PropertyDefinition;
Expand Down Expand Up @@ -52,14 +50,11 @@ protected boolean setProperty(
case "property": {
java.util.List<PropertyDefinition> val
= asFlatList(node, PropertyDefinition.class);
target.setProperties(val);
target.setPropertyDefinitions(val);
break;
}
case "properties": {
target.setProperties(
asMap(node).entrySet().stream()
.map(e -> new PropertyDefinition(e.getKey(), (String) e.getValue()))
.collect(Collectors.toList()));
target.setProperties(asMap(node));
break;
}
case "script": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
@YamlProperty(name = "parameters",
type = "array:org.apache.camel.model.RouteTemplateParameterDefinition"),
@YamlProperty(name = "beans",
type = "array:org.apache.camel.model.app.RegistryBeanDefinition")
type = "array:org.apache.camel.model.RouteTemplateBeanDefinition")
})
public class RouteTemplateDefinitionDeserializer extends YamlDeserializerBase<RouteTemplateDefinition> {
public RouteTemplateDefinitionDeserializer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
@YamlProperty(name = "parameters",
type = "array:org.apache.camel.model.TemplatedRouteParameterDefinition"),
@YamlProperty(name = "beans",
type = "array:org.apache.camel.model.app.RegistryBeanDefinition")
type = "array:org.apache.camel.model.TemplatedRouteBeanDefinition")
})
public class TemplatedRouteDefinitionDeserializer extends YamlDeserializerBase<TemplatedRouteDefinition> {
public TemplatedRouteDefinitionDeserializer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2553,7 +2553,7 @@
"beans" : {
"type" : "array",
"items" : {
"$ref" : "#/items/definitions/org.apache.camel.model.app.RegistryBeanDefinition"
"$ref" : "#/items/definitions/org.apache.camel.model.RouteTemplateBeanDefinition"
}
},
"from" : {
Expand Down Expand Up @@ -3022,7 +3022,7 @@
"beans" : {
"type" : "array",
"items" : {
"$ref" : "#/items/definitions/org.apache.camel.model.app.RegistryBeanDefinition"
"$ref" : "#/items/definitions/org.apache.camel.model.TemplatedRouteBeanDefinition"
}
},
"parameters" : {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2463,7 +2463,7 @@
"beans" : {
"type" : "array",
"items" : {
"$ref" : "#/items/definitions/org.apache.camel.model.app.RegistryBeanDefinition"
"$ref" : "#/items/definitions/org.apache.camel.model.RouteTemplateBeanDefinition"
}
},
"from" : {
Expand Down Expand Up @@ -2932,7 +2932,7 @@
"beans" : {
"type" : "array",
"items" : {
"$ref" : "#/items/definitions/org.apache.camel.model.app.RegistryBeanDefinition"
"$ref" : "#/items/definitions/org.apache.camel.model.TemplatedRouteBeanDefinition"
}
},
"parameters" : {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ class KameletLoaderTest extends YamlTestSupport {
x-descriptors:
- 'urn:alm:descriptor:com.tectonic.ui:checkbox'
template:
beans:
- name: kameletBean
type: org.apache.camel.dsl.yaml.KameletBean
property:
- key: kbProp
value: kbValue
- name: kameletBean2
type: org.apache.camel.dsl.yaml.KameletBean
properties:
kbProp2: kbValue2
kbObjProp:
kbObjPropProp: kbObjPropPropVal
from:
uri: "kamelet:source"
steps:
Expand Down Expand Up @@ -93,11 +105,11 @@ class KameletLoaderTest extends YamlTestSupport {

with(route) {
input.endpointUri == 'kamelet:source'
input.lineNumber == 35
input.lineNumber == 47
outputs.size() == 1
with (outputs[0], ToDefinition) {
endpointUri ==~ /aws2-s3:.*/
lineNumber == 38
lineNumber == 50
}
}
}
Expand Down

0 comments on commit be5dd92

Please sign in to comment.