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 99fe001 Camel 16607 (#5550)
99fe001 is described below
commit 99fe001098fddd1d71aa78cb7523390a18977b8b
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu May 13 12:41:37 2021 +0200
Camel 16607 (#5550)
CAMEL-16607: route template / kamelets. Allow to use #type and #class for
local beans
---
.../apache/camel/catalog/models/templateBean.json | 4 +-
.../kamelet/KameletLocalBeanClassFourTest.java | 81 ++++++++++
.../kamelet/KameletLocalBeanClassTest.java | 69 +++++++++
.../kamelet/KameletLocalBeanClassThreeTest.java | 81 ++++++++++
.../kamelet/KameletLocalBeanClassTwoTest.java | 69 +++++++++
.../kamelet/KameletLocalBeanTypeTest.java | 77 ++++++++++
.../camel/spring/routebuilder/MyLocalBean.java | 34 +++++
.../SpringRouteTemplateLocalBeanTest.java | 51 +++++++
.../SpringRouteTemplateLocalBeanTest.xml | 46 ++++++
.../main/java/org/apache/camel/spi/Registry.java | 4 +-
.../java/org/apache/camel/impl/DefaultModel.java | 121 +++++++++++++--
.../services/org/apache/camel/model.properties | 1 +
.../resources/org/apache/camel/model/jaxb.index | 1 +
.../org/apache/camel/model/templateBean.json | 5 +-
.../org/apache/camel/model/templateScript.json | 16 ++
.../camel/model/RouteTemplateBeanDefinition.java | 164 +++++++++++++++++----
.../camel/model/RouteTemplateDefinition.java | 7 +-
.../camel/model/RouteTemplateScriptDefinition.java | 53 +++++++
.../camel/builder/RouteTemplateLocalBeanTest.java | 85 ++++++++++-
.../org/apache/camel/support/DefaultRegistry.java | 20 ++-
.../org/apache/camel/support/SimpleRegistry.java | 4 +-
.../org/apache/camel/support/SupplierRegistry.java | 4 +-
.../java/org/apache/camel/xml/in/ModelParser.java | 15 +-
.../modules/ROOT/pages/route-template.adoc | 95 ++++++++++--
.../dsl/yaml/deserializers/ModelDeserializers.java | 58 ++++++--
.../deserializers/ModelDeserializersResolver.java | 2 +
.../src/generated/resources/camel-yaml-dsl.json | 24 ++-
27 files changed, 1102 insertions(+), 89 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/templateBean.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/templateBean.json
index d2b0b7c..0efe577 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/templateBean.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/templateBean.json
@@ -12,7 +12,7 @@
},
"properties": {
"name": { "kind": "attribute", "displayName": "Name", "required": true,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Bean name" },
- "language": { "kind": "attribute", "displayName": "Language", "required":
false, "type": "enum", "javaType": "java.lang.String", "enum": [ "bean",
"groovy", "joor", "language", "mvel", "ognl" ], "deprecated": false,
"autowired": false, "secret": false, "description": "The language to use for
creating the bean (such as groovy, joor)" },
- "script": { "kind": "value", "displayName": "Script", "required": true,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "The script to execute that
creates the bean. 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." }
+ "type": { "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
[...]
+ "script": { "kind": "value", "displayName": "Script", "required": true,
"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." }
}
}
diff --git
a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanClassFourTest.java
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanClassFourTest.java
new file mode 100644
index 0000000..3b254b3
--- /dev/null
+++
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanClassFourTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.component.kamelet;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.apache.http.annotation.Obsolete;
+import org.junit.jupiter.api.Test;
+
+public class KameletLocalBeanClassFourTest extends CamelTestSupport {
+
+ @Test
+ public void testOne() throws Exception {
+ getMockEndpoint("mock:result").expectedBodiesReceived("Hi John we are
going to Moes");
+
+ template.sendBody("direct:bar", "John");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ // **********************************************
+ //
+ // test set-up
+ //
+ // **********************************************
+
+ @Obsolete
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ routeTemplate("whereTo")
+ .templateParameter("bar")
+ .templateBean("myBar")
+
.type("org.apache.camel.component.kamelet.KameletLocalBeanClassFourTest$MyBar")
+ .property("bar", "{{bar}}")
+ .end()
+ .from("kamelet:source")
+ // must use {{myBar}} to refer to the local bean
+ .to("bean:{{myBar}}");
+
+ from("direct:bar")
+ .kamelet("whereTo?bar=Moes")
+ .to("mock:result");
+ }
+ };
+ }
+
+ public static class MyBar {
+
+ private String bar;
+
+ public String getBar() {
+ return bar;
+ }
+
+ public void setBar(String bar) {
+ this.bar = bar;
+ }
+
+ public String where(String name) {
+ return "Hi " + name + " we are going to " + bar;
+ }
+ }
+
+}
diff --git
a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanClassTest.java
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanClassTest.java
new file mode 100644
index 0000000..b692401
--- /dev/null
+++
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanClassTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.component.kamelet;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.apache.http.annotation.Obsolete;
+import org.junit.jupiter.api.Test;
+
+public class KameletLocalBeanClassTest extends CamelTestSupport {
+
+ @Test
+ public void testOne() throws Exception {
+ getMockEndpoint("mock:result").expectedBodiesReceived("Hi John we are
going to Murphys");
+
+ template.sendBody("direct:bar", "John");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ // **********************************************
+ //
+ // test set-up
+ //
+ // **********************************************
+
+ @Obsolete
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ routeTemplate("whereTo")
+ .templateBean("myBar", MyBar.class)
+ .from("kamelet:source")
+ // must use {{myBar}} to refer to the local bean
+ .to("bean:{{myBar}}");
+
+ from("direct:bar")
+ .kamelet("whereTo")
+ .to("mock:result");
+ }
+ };
+ }
+
+ public static class MyBar {
+
+ private final String bar = "Murphys";
+
+ public String where(String name) {
+ return "Hi " + name + " we are going to " + bar;
+ }
+ }
+
+}
diff --git
a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanClassThreeTest.java
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanClassThreeTest.java
new file mode 100644
index 0000000..5d4b3f8
--- /dev/null
+++
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanClassThreeTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.component.kamelet;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.apache.http.annotation.Obsolete;
+import org.junit.jupiter.api.Test;
+
+public class KameletLocalBeanClassThreeTest extends CamelTestSupport {
+
+ @Test
+ public void testOne() throws Exception {
+ getMockEndpoint("mock:result").expectedBodiesReceived("Hi John we are
going to Moes");
+
+ template.sendBody("direct:bar", "John");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ // **********************************************
+ //
+ // test set-up
+ //
+ // **********************************************
+
+ @Obsolete
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ routeTemplate("whereTo")
+ .templateParameter("bar")
+ .templateBean("myBar")
+ .type(MyBar.class)
+ .property("bar", "{{bar}}")
+ .end()
+ .from("kamelet:source")
+ // must use {{myBar}} to refer to the local bean
+ .to("bean:{{myBar}}");
+
+ from("direct:bar")
+ .kamelet("whereTo?bar=Moes")
+ .to("mock:result");
+ }
+ };
+ }
+
+ public static class MyBar {
+
+ private String bar;
+
+ public String getBar() {
+ return bar;
+ }
+
+ public void setBar(String bar) {
+ this.bar = bar;
+ }
+
+ public String where(String name) {
+ return "Hi " + name + " we are going to " + bar;
+ }
+ }
+
+}
diff --git
a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanClassTwoTest.java
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanClassTwoTest.java
new file mode 100644
index 0000000..e57760b
--- /dev/null
+++
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanClassTwoTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.component.kamelet;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.apache.http.annotation.Obsolete;
+import org.junit.jupiter.api.Test;
+
+public class KameletLocalBeanClassTwoTest extends CamelTestSupport {
+
+ @Test
+ public void testOne() throws Exception {
+ getMockEndpoint("mock:result").expectedBodiesReceived("Hi John we are
going to Murphys");
+
+ template.sendBody("direct:bar", "John");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ // **********************************************
+ //
+ // test set-up
+ //
+ // **********************************************
+
+ @Obsolete
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ routeTemplate("whereTo")
+ .templateBean("myBar",
"#class:org.apache.camel.component.kamelet.KameletLocalBeanClassTwoTest$MyBar")
+ .from("kamelet:source")
+ // must use {{myBar}} to refer to the local bean
+ .to("bean:{{myBar}}");
+
+ from("direct:bar")
+ .kamelet("whereTo")
+ .to("mock:result");
+ }
+ };
+ }
+
+ public static class MyBar {
+
+ private final String bar = "Murphys";
+
+ public String where(String name) {
+ return "Hi " + name + " we are going to " + bar;
+ }
+ }
+
+}
diff --git
a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanTypeTest.java
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanTypeTest.java
new file mode 100644
index 0000000..70d00d2
--- /dev/null
+++
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletLocalBeanTypeTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.component.kamelet;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.apache.http.annotation.Obsolete;
+import org.junit.jupiter.api.Test;
+
+public class KameletLocalBeanTypeTest extends CamelTestSupport {
+
+ @BindToRegistry("myBar")
+ private MyBar bar = new MyBar();
+
+ @Test
+ public void testOne() throws Exception {
+ getMockEndpoint("mock:result").expectedBodiesReceived("Hi John we are
going to Murphys");
+
+ template.sendBody("direct:bar", "John");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ // **********************************************
+ //
+ // test set-up
+ //
+ // **********************************************
+
+ @Obsolete
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ routeTemplate("whereTo")
+ .templateBean("myBar",
"#type:org.apache.camel.component.kamelet.KameletLocalBeanTypeTest$Bar")
+ .from("kamelet:source")
+ // must use {{myBar}} to refer to the local bean
+ .to("bean:{{myBar}}");
+
+ from("direct:bar")
+ .kamelet("whereTo")
+ .to("mock:result");
+ }
+ };
+ }
+
+ public interface Bar {
+ String where(String name);
+ }
+
+ public static class MyBar implements Bar {
+
+ private final String bar = "Murphys";
+
+ public String where(String name) {
+ return "Hi " + name + " we are going to " + bar;
+ }
+ }
+
+}
diff --git
a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/routebuilder/MyLocalBean.java
b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/routebuilder/MyLocalBean.java
new file mode 100644
index 0000000..47fed85
--- /dev/null
+++
b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/routebuilder/MyLocalBean.java
@@ -0,0 +1,34 @@
+/*
+ * 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.spring.routebuilder;
+
+public class MyLocalBean {
+
+ private String prefix;
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public String hello(String body) {
+ return prefix + " " + body;
+ }
+}
diff --git
a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/routebuilder/SpringRouteTemplateLocalBeanTest.java
b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/routebuilder/SpringRouteTemplateLocalBeanTest.java
new file mode 100644
index 0000000..7f0eba5
--- /dev/null
+++
b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/routebuilder/SpringRouteTemplateLocalBeanTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.spring.routebuilder;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.SpringTestSupport;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.support.AbstractXmlApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+public class SpringRouteTemplateLocalBeanTest extends SpringTestSupport {
+
+ @Override
+ protected AbstractXmlApplicationContext createApplicationContext() {
+ return new
ClassPathXmlApplicationContext("org/apache/camel/spring/routebuilder/SpringRouteTemplateLocalBeanTest.xml");
+ }
+
+ @Test
+ public void testLocalBean() throws Exception {
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("foo", "one");
+ parameters.put("bar", "cheese");
+ parameters.put("greeting", "Davs");
+ context.addRouteFromTemplate("first", "myTemplate", parameters);
+
+ MockEndpoint mock = getMockEndpoint("mock:cheese");
+ mock.expectedBodiesReceived("Davs World");
+
+ template.sendBody("direct:one", "World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+}
diff --git
a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/routebuilder/SpringRouteTemplateLocalBeanTest.xml
b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/routebuilder/SpringRouteTemplateLocalBeanTest.xml
new file mode 100644
index 0000000..73b7708
--- /dev/null
+++
b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/routebuilder/SpringRouteTemplateLocalBeanTest.xml
@@ -0,0 +1,46 @@
+<?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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xsi:schemaLocation="
+ http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd
+ http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
+
+ <camelContext id="foo" xmlns="http://camel.apache.org/schema/spring">
+ <routeTemplate id="myTemplate">
+ <description>blah blah</description>
+ <templateParameter name="foo"/>
+ <templateParameter name="bar"/>
+ <templateParameter name="greeting"/>
+ <templateBean name="myBean"
type="#class:org.apache.camel.spring.routebuilder.MyLocalBean">
+
+ <property key="prefix" value="{{greeting}}"/>
+ </templateBean>
+ <route>
+ <from uri="direct:{{foo}}"/>
+ <to uri="bean:{{myBean}}"/>
+ <to uri="mock:{{bar}}"/>
+ </route>
+ </routeTemplate>
+ </camelContext>
+
+</beans>
\ No newline at end of file
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/Registry.java
b/core/camel-api/src/main/java/org/apache/camel/spi/Registry.java
index a15a465..683d8aa 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/Registry.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/Registry.java
@@ -36,7 +36,9 @@ public interface Registry extends BeanRepository {
* @throws RuntimeCamelException is thrown if binding is not possible
*/
default void bind(String id, Object bean) throws RuntimeCamelException {
- bind(id, bean.getClass(), bean);
+ if (bean != null) {
+ bind(id, bean.getClass(), bean);
+ }
}
/**
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 53b02cc..4311416 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
@@ -22,15 +22,19 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import org.apache.camel.CamelContext;
+import org.apache.camel.Component;
import org.apache.camel.Exchange;
import org.apache.camel.Expression;
import org.apache.camel.ExtendedCamelContext;
import org.apache.camel.FailedToCreateRouteFromTemplateException;
+import org.apache.camel.NoSuchBeanException;
+import org.apache.camel.PropertyBindingException;
import org.apache.camel.RouteTemplateContext;
import org.apache.camel.model.DataFormatDefinition;
import org.apache.camel.model.DefaultRouteTemplateContext;
@@ -55,9 +59,13 @@ import org.apache.camel.model.validator.ValidatorDefinition;
import org.apache.camel.spi.ExchangeFactory;
import org.apache.camel.spi.Language;
import org.apache.camel.spi.ModelReifierFactory;
+import org.apache.camel.spi.PropertyConfigurer;
import org.apache.camel.spi.ScriptingLanguage;
+import org.apache.camel.support.PropertyBindingSupport;
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;
public class DefaultModel implements Model {
@@ -336,16 +344,22 @@ public class DefaultModel implements Model {
private void addTemplateBeans(RouteTemplateContext routeTemplateContext,
RouteTemplateDefinition target) throws Exception {
for (RouteTemplateBeanDefinition b : target.getTemplateBeans()) {
+ final Map<String, Object> props = new HashMap<>();
+ if (b.getProperties() != null) {
+ b.getProperties().forEach(p -> props.put(p.getKey(),
p.getValue()));
+ }
if (b.getBeanSupplier() != null) {
- // bean class is optional for supplier
- if (b.getBeanClass() != null) {
- routeTemplateContext.bind(b.getName(), b.getBeanClass(),
b.getBeanSupplier());
- } else {
- routeTemplateContext.bind(b.getName(),
b.getBeanSupplier());
+ if (props.isEmpty()) {
+ // bean class is optional for supplier
+ if (b.getBeanClass() != null) {
+ routeTemplateContext.bind(b.getName(),
b.getBeanClass(), b.getBeanSupplier());
+ } else {
+ routeTemplateContext.bind(b.getName(),
b.getBeanSupplier());
+ }
}
} else if (b.getScript() != null) {
- final String script = b.getScript();
- final Language lan =
camelContext.resolveLanguage(b.getLanguage());
+ final String script = b.getScript().getScript();
+ final Language lan = camelContext.resolveLanguage(b.getType());
final Class<?> clazz = b.getBeanClass() != null ?
b.getBeanClass() : Object.class;
final ScriptingLanguage slan = lan instanceof
ScriptingLanguage ? (ScriptingLanguage) lan : null;
if (slan != null) {
@@ -354,7 +368,11 @@ public class DefaultModel implements Model {
Map<String, Object> bindings = new HashMap<>();
// use rtx as the short-hand name, as context would
imply its CamelContext
bindings.put("rtc", routeTemplateContext);
- return slan.evaluate(script, bindings, clazz);
+ Object local = slan.evaluate(script, bindings, clazz);
+ if (!props.isEmpty()) {
+ setPropertiesOnTarget(camelContext, local, props);
+ }
+ return local;
});
} else {
// exchange based languages needs a dummy exchange to be
evaluated
@@ -365,7 +383,11 @@ public class DefaultModel implements Model {
String text =
ScriptHelper.resolveOptionalExternalScript(camelContext, dummy, script);
if (text != null) {
Expression exp = lan.createExpression(text);
- return exp.evaluate(dummy, clazz);
+ Object local = exp.evaluate(dummy, clazz);
+ if (!props.isEmpty()) {
+ setPropertiesOnTarget(camelContext, local,
props);
+ }
+ return local;
} else {
return null;
}
@@ -374,10 +396,30 @@ public class DefaultModel implements Model {
}
});
}
- } else if (b.getBeanClass() != null) {
+ } 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
- routeTemplateContext.bind(b.getName(), b.getBeanClass(),
- () ->
camelContext.getInjector().newInstance(b.getBeanClass()));
+ routeTemplateContext.bind(b.getName(), clazz,
+ () -> {
+ 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);
+ if (found == null || found.isEmpty()) {
+ throw new NoSuchBeanException(null, clazz.getName());
+ } else if (found.size() > 1) {
+ throw new NoSuchBeanException(
+ "Found " + found.size() + " beans of type: " +
clazz + ". Only one bean expected.");
+ } else {
+ // do not set properties when using #type as it uses an
existing shared bean
+ routeTemplateContext.bind(b.getName(), clazz,
found.iterator().next());
+ }
}
}
}
@@ -621,4 +663,59 @@ public class DefaultModel implements Model {
}
}
+ private static void setPropertiesOnTarget(CamelContext context, Object
target, Map<String, Object> properties) {
+ ObjectHelper.notNull(context, "context");
+ ObjectHelper.notNull(target, "target");
+ ObjectHelper.notNull(properties, "properties");
+
+ if (target instanceof CamelContext) {
+ throw new UnsupportedOperationException("Configuring the Camel
Context is not supported");
+ }
+
+ PropertyConfigurer configurer = null;
+ if (target instanceof Component) {
+ // the component needs to be initialized to have the configurer
ready
+ ServiceHelper.initService(target);
+ configurer = ((Component) target).getComponentPropertyConfigurer();
+ }
+
+ if (configurer == null) {
+ // see if there is a configurer for it
+ configurer = context.adapt(ExtendedCamelContext.class)
+ .getConfigurerResolver()
+
.resolvePropertyConfigurer(target.getClass().getSimpleName(), context);
+ }
+
+ try {
+ PropertyBindingSupport.build()
+ .withMandatory(true)
+ .withRemoveParameters(false)
+ .withConfigurer(configurer)
+ .withIgnoreCase(true)
+ .withFlattenProperties(true)
+ .bind(context, target, properties);
+ } catch (PropertyBindingException e) {
+ String key = e.getOptionKey();
+ if (key == null) {
+ String prefix = e.getOptionPrefix();
+ if (prefix != null && !prefix.endsWith(".")) {
+ prefix = "." + prefix;
+ }
+
+ key = prefix != null
+ ? prefix + "." + e.getPropertyName()
+ : e.getPropertyName();
+ }
+
+ // enrich the error with more precise details with option prefix
and key
+ throw new PropertyBindingException(
+ e.getTarget(),
+ e.getPropertyName(),
+ e.getValue(),
+ null,
+ key,
+ e.getCause());
+ }
+ }
+
}
diff --git
a/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties
b/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties
index 3e57411..b94ed3b 100644
---
a/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties
+++
b/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties
@@ -172,6 +172,7 @@ tarfile
templateBean
templateBeanFactory
templateParameter
+templateScript
threadPoolProfile
threads
thrift
diff --git
a/core/camel-core-model/src/generated/resources/org/apache/camel/model/jaxb.index
b/core/camel-core-model/src/generated/resources/org/apache/camel/model/jaxb.index
index 88bee1b..c134380 100644
---
a/core/camel-core-model/src/generated/resources/org/apache/camel/model/jaxb.index
+++
b/core/camel-core-model/src/generated/resources/org/apache/camel/model/jaxb.index
@@ -69,6 +69,7 @@ RouteTemplateBeanDefinition
RouteTemplateContextRefDefinition
RouteTemplateDefinition
RouteTemplateParameterDefinition
+RouteTemplateScriptDefinition
RouteTemplatesDefinition
RoutesDefinition
RoutingSlipDefinition
diff --git
a/core/camel-core-model/src/generated/resources/org/apache/camel/model/templateBean.json
b/core/camel-core-model/src/generated/resources/org/apache/camel/model/templateBean.json
index d2b0b7c..9f2c566 100644
---
a/core/camel-core-model/src/generated/resources/org/apache/camel/model/templateBean.json
+++
b/core/camel-core-model/src/generated/resources/org/apache/camel/model/templateBean.json
@@ -12,7 +12,8 @@
},
"properties": {
"name": { "kind": "attribute", "displayName": "Name", "required": true,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Bean name" },
- "language": { "kind": "attribute", "displayName": "Language", "required":
false, "type": "enum", "javaType": "java.lang.String", "enum": [ "bean",
"groovy", "joor", "language", "mvel", "ognl" ], "deprecated": false,
"autowired": false, "secret": false, "description": "The language to use for
creating the bean (such as groovy, joor)" },
- "script": { "kind": "value", "displayName": "Script", "required": true,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "The script to execute that
creates the bean. 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." }
+ "type": { "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
[...]
+ "property": { "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": { "kind": "element", "displayName": "Script", "required": false,
"type": "object", "javaType":
"org.apache.camel.model.RouteTemplateScriptDefinition", "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." }
}
}
diff --git
a/core/camel-core-model/src/generated/resources/org/apache/camel/model/templateScript.json
b/core/camel-core-model/src/generated/resources/org/apache/camel/model/templateScript.json
new file mode 100644
index 0000000..2180976
--- /dev/null
+++
b/core/camel-core-model/src/generated/resources/org/apache/camel/model/templateScript.json
@@ -0,0 +1,16 @@
+{
+ "model": {
+ "kind": "model",
+ "name": "templateScript",
+ "title": "Template Script",
+ "description": "A route template script (local bean) when using scripting
languages such as groovy",
+ "deprecated": false,
+ "label": "configuration",
+ "javaType": "org.apache.camel.model.RouteTemplateScriptDefinition",
+ "input": false,
+ "output": false
+ },
+ "properties": {
+ "script": { "kind": "value", "displayName": "Script", "required": true,
"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." }
+ }
+}
diff --git
a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateBeanDefinition.java
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateBeanDefinition.java
index 84bd5a7..79876d3 100644
---
a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateBeanDefinition.java
+++
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateBeanDefinition.java
@@ -16,12 +16,16 @@
*/
package org.apache.camel.model;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
-import javax.xml.bind.annotation.XmlValue;
import org.apache.camel.RouteTemplateContext;
import org.apache.camel.spi.Metadata;
@@ -37,17 +41,16 @@ public class RouteTemplateBeanDefinition {
private RouteTemplateDefinition parent;
@XmlAttribute(required = true)
private String name;
- @XmlTransient
- private Class<?> beanClass;
- // it only makes sense to use the languages that are general purpose
scripting languages
@XmlAttribute(required = true)
- @Metadata(enums = "bean,groovy,joor,language,mvel,ognl")
- private String language;
- @XmlValue
- @Metadata(required = true)
- private String script;
+ private String type;
+ @XmlElement(name = "property")
+ private List<PropertyDefinition> properties;
+ @XmlElement
+ private RouteTemplateScriptDefinition script;
// special for java-dsl to allow using lambda style
@XmlTransient
+ private Class<?> beanClass;
+ @XmlTransient
private RouteTemplateContext.BeanSupplier<Object> beanSupplier;
public RouteTemplateBeanDefinition() {
@@ -80,29 +83,31 @@ public class RouteTemplateBeanDefinition {
this.beanClass = beanType;
}
- public String getLanguage() {
- return language;
+ public String getType() {
+ return type;
}
/**
- * The language to use for creating the bean (such as groovy, joor)
+ * 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.
*/
- public void setLanguage(String language) {
- this.language = language;
+ public void setType(String type) {
+ this.type = type;
}
- public String getScript() {
- return script;
+ public List<PropertyDefinition> getProperties() {
+ return properties;
}
/**
- * The script to execute that creates the bean.
- *
- * If the script use the prefix <tt>resource:</tt> such as
<tt>resource:classpath:com/foo/myscript.groovy</tt>,
- * <tt>resource:file:/var/myscript.groovy</tt>, then its loaded from the
external resource.
+ * Optional properties to set on the created local bean
*/
- public void setScript(String script) {
- this.script = script;
+ public void setProperties(List<PropertyDefinition> properties) {
+ this.properties = properties;
}
public RouteTemplateContext.BeanSupplier<Object> getBeanSupplier() {
@@ -116,10 +121,56 @@ public class RouteTemplateBeanDefinition {
this.beanSupplier = beanSupplier;
}
+ public RouteTemplateScriptDefinition getScript() {
+ return script;
+ }
+
+ /**
+ * The script to execute that creates the bean when using scripting
languages.
+ *
+ * If the script use the prefix <tt>resource:</tt> such as
<tt>resource:classpath:com/foo/myscript.groovy</tt>,
+ * <tt>resource:file:/var/myscript.groovy</tt>, then its loaded from the
external resource.
+ */
+ public void setScript(RouteTemplateScriptDefinition script) {
+ this.script = script;
+ }
+
+ /**
+ * The script to execute that creates the bean when using scripting
languages.
+ *
+ * If the script use the prefix <tt>resource:</tt> such as
<tt>resource:classpath:com/foo/myscript.groovy</tt>,
+ * <tt>resource:file:/var/myscript.groovy</tt>, then its loaded from the
external resource.
+ */
+ public void setScript(String script) {
+ this.script = new RouteTemplateScriptDefinition();
+ this.script.setScript(script);
+ }
+
// fluent builders
// ----------------------------------------------------
/**
+ * Creates the bean from the given class type
+ *
+ * @param type the type of the class to create as bean
+ */
+ public RouteTemplateDefinition beanClass(Class<?> type) {
+ setType("#class:" + type.getName());
+ return parent;
+ }
+
+ /**
+ * Lookup in the registry for bean instances of the given type, and if
there is a single instance of the given type,
+ * then that bean will be used as the local bean (danger this bean is
shared)
+ *
+ * @param type the type of the class to lookup in the registry
+ */
+ public RouteTemplateDefinition beanType(Class<?> type) {
+ setType("#type:" + type.getName());
+ return parent;
+ }
+
+ /**
* Calls a method on a bean for creating the local template bean
*
* @param type the bean class to call
@@ -135,7 +186,7 @@ public class RouteTemplateBeanDefinition {
* @param method the name of the method to call
*/
public RouteTemplateDefinition bean(Class<?> type, String method) {
- setLanguage("bean");
+ setType("bean");
if (method != null) {
setScript(type.getName() + "?method=" + method);
} else {
@@ -153,7 +204,7 @@ public class RouteTemplateBeanDefinition {
* @param script the script
*/
public RouteTemplateDefinition groovy(String script) {
- setLanguage("groovy");
+ setType("groovy");
setScript(script);
return parent;
}
@@ -167,7 +218,7 @@ public class RouteTemplateBeanDefinition {
* @param script the script
*/
public RouteTemplateDefinition joor(String script) {
- setLanguage("joor");
+ setType("joor");
setScript(script);
return parent;
}
@@ -178,16 +229,42 @@ public class RouteTemplateBeanDefinition {
* If the script use the prefix <tt>resource:</tt> such as
<tt>resource:classpath:com/foo/myscript.groovy</tt>,
* <tt>resource:file:/var/myscript.groovy</tt>, then its loaded from the
external resource.
*
- * @param language the custom language (a language not provided out of the
box from Camel)
+ * @param language the language
* @param script the script
*/
public RouteTemplateDefinition language(String language, String script) {
- setLanguage(language);
+ setType(language);
setScript(script);
return parent;
}
/**
+ * 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.
+ */
+ public RouteTemplateBeanDefinition type(String type) {
+ if (!type.startsWith("#type:") && !type.startsWith("#class:")) {
+ type = "#class:" + type;
+ }
+ setType(type);
+ return this;
+ }
+
+ /**
+ * Creates the bean from the given class type
+ *
+ * @param type the type of the class to create as bean
+ */
+ public RouteTemplateBeanDefinition type(Class<?> type) {
+ beanClass(type);
+ return this;
+ }
+
+ /**
* Calls a MvEL script for creating the local template bean
*
* If the script use the prefix <tt>resource:</tt> such as
<tt>resource:classpath:com/foo/myscript.groovy</tt>,
@@ -196,7 +273,7 @@ public class RouteTemplateBeanDefinition {
* @param script the script
*/
public RouteTemplateDefinition mvel(String script) {
- setLanguage("mvel");
+ setType("mvel");
setScript(script);
return parent;
}
@@ -210,9 +287,38 @@ public class RouteTemplateBeanDefinition {
* @param script the script
*/
public RouteTemplateDefinition ognl(String script) {
- setLanguage("ognl");
+ setType("ognl");
setScript(script);
return parent;
}
+ /**
+ * Sets a property to set on the created local bean
+ *
+ * @param key the property name
+ * @param value the property value
+ */
+ public RouteTemplateBeanDefinition property(String key, String value) {
+ if (properties == null) {
+ properties = new ArrayList<>();
+ }
+ properties.add(new PropertyDefinition(key, value));
+ return this;
+ }
+
+ /**
+ * Sets properties to set on the created local bean
+ */
+ public RouteTemplateBeanDefinition properties(Map<String, String>
properties) {
+ if (this.properties == null) {
+ this.properties = new ArrayList<>();
+ }
+ properties.forEach((k, v) -> this.properties.add(new
PropertyDefinition(k, v)));
+ return this;
+ }
+
+ public RouteTemplateDefinition end() {
+ return parent;
+ }
+
}
diff --git
a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
index 987b635..26ae044 100644
---
a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
+++
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
@@ -223,7 +223,7 @@ public class RouteTemplateDefinition extends
OptionalIdentifiedDefinition {
* Adds a local bean the route template uses
*
* @param name the name of the bean
- * @param bean the bean or a supplier for the bean
+ * @param bean the bean, or reference to bean (#class or #type), or a
supplier for the bean
*/
@SuppressWarnings("unchecked")
public RouteTemplateDefinition templateBean(String name, Object bean) {
@@ -236,6 +236,9 @@ public class RouteTemplateDefinition extends
OptionalIdentifiedDefinition {
def.setBeanSupplier((RouteTemplateContext.BeanSupplier<Object>)
bean);
} else if (bean instanceof Supplier) {
def.setBeanSupplier(ctx -> ((Supplier<?>) bean).get());
+ } else if (bean instanceof String) {
+ // its a string type
+ def.setType((String) bean);
} else {
def.setBeanSupplier(ctx -> bean);
}
@@ -292,7 +295,7 @@ public class RouteTemplateDefinition extends
OptionalIdentifiedDefinition {
}
RouteTemplateBeanDefinition def = new RouteTemplateBeanDefinition();
def.setName(name);
- def.setLanguage(language);
+ def.setType(language);
def.setScript(script);
templateBeans.add(def);
return this;
diff --git
a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateScriptDefinition.java
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateScriptDefinition.java
new file mode 100644
index 0000000..b4d583e
--- /dev/null
+++
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateScriptDefinition.java
@@ -0,0 +1,53 @@
+/*
+ * 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.model;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlValue;
+
+import org.apache.camel.spi.Metadata;
+
+/**
+ * A route template script (local bean) when using scripting languages such as
groovy
+ */
+@Metadata(label = "configuration")
+@XmlRootElement(name = "templateScript")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class RouteTemplateScriptDefinition {
+ @XmlValue
+ private String script;
+
+ public RouteTemplateScriptDefinition() {
+ }
+
+ public String getScript() {
+ return script;
+ }
+
+ /**
+ * The script to execute that creates the bean when using scripting
languages.
+ *
+ * If the script use the prefix <tt>resource:</tt> such as
<tt>resource:classpath:com/foo/myscript.groovy</tt>,
+ * <tt>resource:file:/var/myscript.groovy</tt>, then its loaded from the
external resource.
+ */
+ public void setScript(String script) {
+ this.script = script;
+ }
+
+}
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 4f50fdf..ef50ef3 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
@@ -548,7 +548,38 @@ public class RouteTemplateLocalBeanTest extends
ContextTestSupport {
context.stop();
}
- private class BuilderProcessor implements Processor {
+ @Test
+ public void testLocalBeanClassExpressionFluent() throws Exception {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+
routeTemplate("myTemplate").templateParameter("foo").templateParameter("bar")
+
.templateBean("myBar").beanClass(BuilderProcessor.class)
+ .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("Builder World", out);
+
+ // should not be a global bean
+ assertNull(context.getRegistry().lookupByName("myBar"));
+
+ context.stop();
+ }
+
+ public static class BuilderProcessor implements Processor {
@Override
public void process(Exchange exchange) throws Exception {
@@ -556,7 +587,39 @@ public class RouteTemplateLocalBeanTest extends
ContextTestSupport {
}
}
- private class BuilderTwoProcessor implements Processor {
+ @Test
+ public void testLocalBeanClassPropertiesFluent() throws Exception {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+
routeTemplate("myTemplate").templateParameter("foo").templateParameter("bar").templateParameter("hi")
+ .templateBean("myBar").property("prefix",
"{{hi}}").beanClass(BuilderThreeProcessor.class)
+ .from("direct:{{foo}}")
+ .to("bean:{{bar}}");
+ }
+ });
+
+ context.start();
+
+ TemplatedRouteBuilder.builder(context, "myTemplate")
+ .parameter("foo", "one")
+ .parameter("bar", "myBar")
+ .parameter("hi", "Davs")
+ .routeId("myRoute")
+ .add();
+
+ assertEquals(1, context.getRoutes().size());
+
+ Object out = template.requestBody("direct:one", "World");
+ assertEquals("DavsBuilder3 World", out);
+
+ // should not be a global bean
+ assertNull(context.getRegistry().lookupByName("myBar"));
+
+ context.stop();
+ }
+
+ public static class BuilderTwoProcessor implements Processor {
private String prefix = "";
@@ -573,6 +636,24 @@ public class RouteTemplateLocalBeanTest extends
ContextTestSupport {
}
}
+ public static class BuilderThreeProcessor implements Processor {
+
+ private String prefix = "";
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ @Override
+ public void process(Exchange exchange) throws Exception {
+ exchange.getMessage().setBody(prefix + "Builder3 " +
exchange.getMessage().getBody());
+ }
+ }
+
public Processor createBuilderProcessor() {
return new BuilderProcessor();
}
diff --git
a/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java
b/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java
index 7864fb7..a714b8b 100644
---
a/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java
+++
b/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java
@@ -165,22 +165,28 @@ public class DefaultRegistry extends ServiceSupport
implements Registry, LocalBe
@Override
public void bind(String id, Class<?> type, Object bean) throws
RuntimeCamelException {
- // automatic inject camel context in bean if its aware
- if (camelContext != null && bean instanceof CamelContextAware) {
- ((CamelContextAware) bean).setCamelContext(camelContext);
+ if (bean != null) {
+ // automatic inject camel context in bean if its aware
+ if (camelContext != null && bean instanceof CamelContextAware) {
+ ((CamelContextAware) bean).setCamelContext(camelContext);
+ }
+ fallbackRegistry.bind(id, type, bean);
}
- fallbackRegistry.bind(id, type, bean);
}
@Override
public void bind(String id, Class<?> type, Supplier<Object> bean) throws
RuntimeCamelException {
- // wrap in cached supplier (memorize)
- supplierRegistry.bind(id, type, Suppliers.memorize(bean));
+ if (bean != null) {
+ // wrap in cached supplier (memorize)
+ supplierRegistry.bind(id, type, Suppliers.memorize(bean));
+ }
}
@Override
public void bindAsPrototype(String id, Class<?> type, Supplier<Object>
bean) throws RuntimeCamelException {
- supplierRegistry.bind(id, type, bean);
+ if (bean != null) {
+ supplierRegistry.bind(id, type, bean);
+ }
}
@Override
diff --git
a/core/camel-support/src/main/java/org/apache/camel/support/SimpleRegistry.java
b/core/camel-support/src/main/java/org/apache/camel/support/SimpleRegistry.java
index 2e99abc..2c57e9b 100644
---
a/core/camel-support/src/main/java/org/apache/camel/support/SimpleRegistry.java
+++
b/core/camel-support/src/main/java/org/apache/camel/support/SimpleRegistry.java
@@ -101,7 +101,9 @@ public class SimpleRegistry extends LinkedHashMap<String,
Map<Class<?>, Object>>
@Override
public void bind(String id, Class type, Object bean) {
- computeIfAbsent(id, k -> new LinkedHashMap<>()).put(type, wrap(bean));
+ if (bean != null) {
+ computeIfAbsent(id, k -> new LinkedHashMap<>()).put(type,
wrap(bean));
+ }
}
@Override
diff --git
a/core/camel-support/src/main/java/org/apache/camel/support/SupplierRegistry.java
b/core/camel-support/src/main/java/org/apache/camel/support/SupplierRegistry.java
index 851847b..a4683d2 100644
---
a/core/camel-support/src/main/java/org/apache/camel/support/SupplierRegistry.java
+++
b/core/camel-support/src/main/java/org/apache/camel/support/SupplierRegistry.java
@@ -112,6 +112,8 @@ public class SupplierRegistry extends SimpleRegistry {
@Override
public void bind(String id, Class<?> type, Supplier<Object> bean) {
- computeIfAbsent(id, k -> new LinkedHashMap<>()).put(type, wrap(bean));
+ if (bean != null) {
+ computeIfAbsent(id, k -> new LinkedHashMap<>()).put(type,
wrap(bean));
+ }
}
}
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 1413e68..b8e7249 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
@@ -1023,12 +1023,23 @@ public class ModelParser extends BaseParser {
protected RouteTemplateBeanDefinition doParseRouteTemplateBeanDefinition()
throws IOException, XmlPullParserException {
return doParse(new RouteTemplateBeanDefinition(), (def, key, val) -> {
switch (key) {
- case "language": def.setLanguage(val); break;
case "name": def.setName(val); break;
+ case "type": def.setType(val); break;
+ default: return false;
+ }
+ return true;
+ }, (def, key) -> {
+ switch (key) {
+ case "property": doAdd(doParsePropertyDefinition(),
def.getProperties(), def::setProperties); break;
+ case "script":
def.setScript(doParseRouteTemplateScriptDefinition()); break;
default: return false;
}
return true;
- }, noElementHandler(), (def, val) -> def.setScript(val));
+ }, noValueHandler());
+ }
+ protected RouteTemplateScriptDefinition
doParseRouteTemplateScriptDefinition() throws IOException,
XmlPullParserException {
+ return doParse(new RouteTemplateScriptDefinition(),
+ noAttributeHandler(), noElementHandler(), (def, val) ->
def.setScript(val));
}
protected RouteTemplateContextRefDefinition
doParseRouteTemplateContextRefDefinition() throws IOException,
XmlPullParserException {
return doParse(new RouteTemplateContextRefDefinition(), (def, key,
val) -> {
diff --git a/docs/user-manual/modules/ROOT/pages/route-template.adoc
b/docs/user-manual/modules/ROOT/pages/route-template.adoc
index f2edf27..cb8afef 100644
--- a/docs/user-manual/modules/ROOT/pages/route-template.adoc
+++ b/docs/user-manual/modules/ROOT/pages/route-template.adoc
@@ -215,6 +215,77 @@ You should prefer to create the local beans directly from
within the template (i
ensures the route template has this out of the box. Otherwise the bean must be
created or provided every time
a new route is created from the route template. However the latter gives
freedom to create the bean in any other custom way.
+=== Binding beans to route templates using bean types
+
+You can create a local bean by referring to a fully qualified class name which
Camel will use to create
+a new local bean instance. When using this the created bean is created via
default constructor of the class.
+
+The bean instance can be configured with properties via getter/setter style.
+The previous example with creating the AWS S3Client would not support this
kind as this uses _fluent builder_ pattern (not getter/setter).
+
+So suppose we have a class as follows
+
+[source,java]
+----
+public class MyBar {
+ private String name;
+ private String address;
+
+ // getter/setter omitted
+
+ public String location() {
+ return "The bar " + name + " is located at " + address;
+ }
+}
+----
+
+Then we can use the `MyBar` class as a local bean in a route template as
follows:
+
+[source,java]
+----
+routeTemplate("barTemplate")
+ .templateParameter("bar")
+ .templateParameter("street")
+ .templateBean("myBar")
+ .type("com.foo.MyBar")
+ .property("name", "{{bar}}")
+ .property("address", "{{street}}")
+ .end()
+ .from("direct:going-out")
+ .to("bean:{{myBar}}")
+----
+
+With Java DSL you can also refer to the bean class using type safe way:
+
+[source,java]
+----
+ .templateBean("myBar")
+ .type(MyBar.class)
+ .property("name", "{{bar}}")
+ .property("address", "{{street}}")
+ .end()
+----
+
+In XML DSL you would do:
+
+[source,xml]
+----
+<camelContext xmlns="http://camel.apache.org/schema/spring">
+ <routeTemplate id="myBar">
+ <templateParameter name="bar"/>
+ <templateParameter name="street"/>
+ <templateBean name="myBean" type="#class:com.foo.MyBar">
+ <property key="name" value="{{bar}}"/>
+ <property key="address" value="{{street}}"/>
+ </templateBean>
+ <route>
+ <from uri="direct:going-out"/>
+ <to uri="bean:{{myBar}}"/>
+ </route>
+ </routeTemplate>
+</camelContext>
+----
+
=== Binding beans to route templates using scripting languages
You can use scripting languages like groovy, joor, mvel which creates the bean.
@@ -268,13 +339,13 @@ The route template in XML DSL can then also use groovy
language to create the be
<routeTemplate id="s3template">
<templateParameter name="region"/>
<templateParameter name="bucket"/>
- <templateBean name="myClient">
- <templateBeanFactory language="groovy">
+ <templateBean name="myClient" type="groovy">
+ <script>
import software.amazon.awssdk.services.s3.S3Client
S3Client.builder()
.region(rtc.getProperty("region", Region.class))
.build()
- </templateBeanFactory>
+ </script>
</templateBean>
<route>
<from uri="direct:s3-store"/>
@@ -293,10 +364,8 @@ the bean creation code to an external file, by using
`resource:` as prefix:
<routeTemplate id="s3template">
<templateParameter name="region"/>
<templateParameter name="bucket"/>
- <templateBean name="myClient">
- <templateBeanFactory language="groovy">
- resource:classpath:s3bean.groovy
- </templateBeanFactory>
+ <templateBean name="myClient" type="groovy">
+ <script>resource:classpath:s3bean.groovy</script>
</templateBean>
<route>
<from uri="direct:s3-store"/>
@@ -310,13 +379,13 @@ The languages supported are:
[width="100%",cols="2s,8",options="header"]
|===
-| Language | Description
-| bean | Calling a method on a Java class.
+| Type | Description
+| bean | Calling a method on a Java class to create the bean.
| groovy | Using groovy script to create the bean.
| joor | Using joor (Java code which are runtime compiled) to create the bean.
-| language | To use a 3rd party language to create the bean.
| mvel | To use Mvel template script to create the bean.
| ognl | To use OGNL template script to create the bean.
+| _name_ | To use a 3rd party language by the given _name_ to create the bean.
|===
Camel will bind `RouteTemplateContext` as the root object with name `rtc` when
evaluating the script.
@@ -354,10 +423,8 @@ then you can call this method from the route template in
XML DSL:
<routeTemplate id="s3template">
<templateParameter name="region"/>
<templateParameter name="bucket"/>
- <templateBean name="myClient">
- <templateBeanFactory language="bean">
- com.foo.MyAwsHelper?method=createS3Client
- </templateBeanFactory>
+ <templateBean name="myClient" type="bean">
+ <script>com.foo.MyAwsHelper?method=createS3Client</script>
</templateBean>
<route>
<from uri="direct:s3-store"/>
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 8992959..c1968d0 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
@@ -73,6 +73,7 @@ import org.apache.camel.model.RouteContextRefDefinition;
import org.apache.camel.model.RouteDefinition;
import org.apache.camel.model.RouteTemplateBeanDefinition;
import org.apache.camel.model.RouteTemplateParameterDefinition;
+import org.apache.camel.model.RouteTemplateScriptDefinition;
import org.apache.camel.model.RoutingSlipDefinition;
import org.apache.camel.model.SagaActionUriDefinition;
import org.apache.camel.model.SagaDefinition;
@@ -11739,9 +11740,10 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
order =
org.apache.camel.dsl.yaml.common.YamlDeserializerResolver.ORDER_LOWEST - 1,
nodes = "template-bean",
properties = {
- @YamlProperty(name = "language", type = "string", required
= true),
@YamlProperty(name = "name", type = "string", required =
true),
- @YamlProperty(name = "script", type = "string", required =
true)
+ @YamlProperty(name = "property", type =
"array:org.apache.camel.model.PropertyDefinition"),
+ @YamlProperty(name = "template-script", type =
"object:org.apache.camel.model.RouteTemplateScriptDefinition"),
+ @YamlProperty(name = "type", type = "string", required =
true)
}
)
public static class RouteTemplateBeanDefinitionDeserializer extends
YamlDeserializerBase<RouteTemplateBeanDefinition> {
@@ -11763,21 +11765,26 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
protected boolean setProperty(RouteTemplateBeanDefinition target,
String propertyKey,
String propertyName, Node node) {
switch(propertyKey) {
- case "language": {
- String val = asText(node);
- target.setLanguage(val);
- break;
- }
case "name": {
String val = asText(node);
target.setName(val);
break;
}
- case "script": {
- String val = asText(node);
+ case "property": {
+ java.util.List<org.apache.camel.model.PropertyDefinition>
val = asFlatList(node, org.apache.camel.model.PropertyDefinition.class);
+ target.setProperties(val);
+ break;
+ }
+ case "template-script": {
+ org.apache.camel.model.RouteTemplateScriptDefinition val =
asType(node, org.apache.camel.model.RouteTemplateScriptDefinition.class);
target.setScript(val);
break;
}
+ case "type": {
+ String val = asText(node);
+ target.setType(val);
+ break;
+ }
default: {
return false;
}
@@ -11840,6 +11847,39 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
}
@YamlType(
+ types = org.apache.camel.model.RouteTemplateScriptDefinition.class,
+ order =
org.apache.camel.dsl.yaml.common.YamlDeserializerResolver.ORDER_LOWEST - 1,
+ nodes = "template-script",
+ properties = @YamlProperty(name = "script", type = "string")
+ )
+ public static class RouteTemplateScriptDefinitionDeserializer extends
YamlDeserializerBase<RouteTemplateScriptDefinition> {
+ public RouteTemplateScriptDefinitionDeserializer() {
+ super(RouteTemplateScriptDefinition.class);
+ }
+
+ @Override
+ protected RouteTemplateScriptDefinition newInstance() {
+ return new RouteTemplateScriptDefinition();
+ }
+
+ @Override
+ protected boolean setProperty(RouteTemplateScriptDefinition target,
String propertyKey,
+ String propertyName, Node node) {
+ switch(propertyKey) {
+ case "script": {
+ String val = asText(node);
+ target.setScript(val);
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ @YamlType(
inline = true,
types = org.apache.camel.model.RoutingSlipDefinition.class,
order =
org.apache.camel.dsl.yaml.common.YamlDeserializerResolver.ORDER_LOWEST - 1,
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializersResolver.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializersResolver.java
index 10d3065..b27616c 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializersResolver.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializersResolver.java
@@ -296,6 +296,8 @@ public final class ModelDeserializersResolver implements
YamlDeserializerResolve
case "org.apache.camel.model.RouteTemplateBeanDefinition": return
new ModelDeserializers.RouteTemplateBeanDefinitionDeserializer();
case "template-parameter": return new
ModelDeserializers.RouteTemplateParameterDefinitionDeserializer();
case "org.apache.camel.model.RouteTemplateParameterDefinition":
return new ModelDeserializers.RouteTemplateParameterDefinitionDeserializer();
+ case "template-script": return new
ModelDeserializers.RouteTemplateScriptDefinitionDeserializer();
+ case "org.apache.camel.model.RouteTemplateScriptDefinition":
return new ModelDeserializers.RouteTemplateScriptDefinitionDeserializer();
case "routing-slip": return new
ModelDeserializers.RoutingSlipDefinitionDeserializer();
case "org.apache.camel.model.RoutingSlipDefinition": return new
ModelDeserializers.RoutingSlipDefinitionDeserializer();
case "rss": return new
ModelDeserializers.RssDataFormatDeserializer();
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json
index 53c4660..d68e8d6 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json
@@ -2065,18 +2065,24 @@
}, {
"type" : "object",
"properties" : {
- "language" : {
- "type" : "string"
- },
"name" : {
"type" : "string"
},
- "script" : {
+ "property" : {
+ "type" : "array",
+ "items" : {
+ "$ref" :
"#/items/definitions/org.apache.camel.model.PropertyDefinition"
+ }
+ },
+ "template-script" : {
+ "$ref" :
"#/items/definitions/org.apache.camel.model.RouteTemplateScriptDefinition"
+ },
+ "type" : {
"type" : "string"
}
}
} ],
- "required" : [ "language", "name", "script" ]
+ "required" : [ "name", "type" ]
},
"org.apache.camel.model.RouteTemplateDefinition" : {
"type" : "object",
@@ -2120,6 +2126,14 @@
},
"required" : [ "name" ]
},
+ "org.apache.camel.model.RouteTemplateScriptDefinition" : {
+ "type" : "object",
+ "properties" : {
+ "script" : {
+ "type" : "string"
+ }
+ }
+ },
"org.apache.camel.model.RoutingSlipDefinition" : {
"oneOf" : [ {
"type" : "string"